Skip to content

Commit

Permalink
rg_display: Moved drivers to seprate files
Browse files Browse the repository at this point in the history
This isn't as nice as the audio system (yet) but I'm not sure if adding indirection to previously inlined functions (get_buffer/send_data) will be a problem...
  • Loading branch information
ducalex committed Aug 1, 2024
1 parent 6b7c1f8 commit ac46ccb
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 282 deletions.
28 changes: 28 additions & 0 deletions components/retro-go/drivers/display/dummy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
static void lcd_init(void)
{
}

static void lcd_deinit(void)
{
}

static void lcd_set_backlight(float percent)
{
}

static inline uint16_t *lcd_get_buffer(size_t length)
{
return (void *)0;
}

static inline void lcd_send_buffer(uint16_t *buffer, size_t length)
{
}

static void lcd_sync(void)
{
}

const rg_display_driver_t rg_display_driver_dummy = {
.name = "dummy",
};
263 changes: 263 additions & 0 deletions components/retro-go/drivers/display/ili9341.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include <driver/ledc.h>

static spi_device_handle_t spi_dev;
static QueueHandle_t spi_transactions;
static QueueHandle_t spi_buffers;

#define SPI_TRANSACTION_COUNT (10)
#define SPI_BUFFER_COUNT (5)
#define SPI_BUFFER_LENGTH (LCD_BUFFER_LENGTH * 2)

static inline uint16_t *spi_take_buffer(void)
{
uint16_t *buffer;
if (xQueueReceive(spi_buffers, &buffer, pdMS_TO_TICKS(2500)) != pdTRUE)
RG_PANIC("display");
return buffer;
}

static inline void spi_give_buffer(uint16_t *buffer)
{
xQueueSend(spi_buffers, &buffer, portMAX_DELAY);
}

static inline void spi_queue_transaction(const void *data, size_t length, uint32_t type)
{
if (!data || !length)
return;

spi_transaction_t *t;
xQueueReceive(spi_transactions, &t, portMAX_DELAY);

*t = (spi_transaction_t){
.tx_buffer = NULL,
.length = length * 8, // In bits
.user = (void *)type,
.flags = 0,
};

if (type & 2)
{
t->tx_buffer = data;
}
else if (length < 5)
{
memcpy(t->tx_data, data, length);
t->flags = SPI_TRANS_USE_TXDATA;
}
else
{
t->tx_buffer = memcpy(spi_take_buffer(), data, length);
t->user = (void *)(type | 2);
}

if (spi_device_queue_trans(spi_dev, t, pdMS_TO_TICKS(2500)) != ESP_OK)
{
RG_PANIC("display");
}
}

IRAM_ATTR
static void spi_pre_transfer_cb(spi_transaction_t *t)
{
// Set the data/command line accordingly
gpio_set_level(RG_GPIO_LCD_DC, (int)t->user & 1);
}

IRAM_ATTR
static void spi_task(void *arg)
{
spi_transaction_t *t;

while (spi_device_get_trans_result(spi_dev, &t, portMAX_DELAY) == ESP_OK)
{
if ((int)t->user & 2)
spi_give_buffer((uint16_t *)t->tx_buffer);
xQueueSend(spi_transactions, &t, portMAX_DELAY);
}
}

static void spi_init(void)
{
spi_transactions = xQueueCreate(SPI_TRANSACTION_COUNT, sizeof(spi_transaction_t *));
spi_buffers = xQueueCreate(SPI_BUFFER_COUNT, sizeof(uint16_t *));

while (uxQueueSpacesAvailable(spi_transactions))
{
void *trans = malloc(sizeof(spi_transaction_t));
xQueueSend(spi_transactions, &trans, portMAX_DELAY);
}

while (uxQueueSpacesAvailable(spi_buffers))
{
void *buffer = rg_alloc(SPI_BUFFER_LENGTH, MEM_DMA);
xQueueSend(spi_buffers, &buffer, portMAX_DELAY);
}

const spi_bus_config_t buscfg = {
.miso_io_num = RG_GPIO_LCD_MISO,
.mosi_io_num = RG_GPIO_LCD_MOSI,
.sclk_io_num = RG_GPIO_LCD_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};

const spi_device_interface_config_t devcfg = {
.clock_speed_hz = RG_SCREEN_SPEED, // Typically SPI_MASTER_FREQ_40M or SPI_MASTER_FREQ_80M
.mode = 0, // SPI mode 0
.spics_io_num = RG_GPIO_LCD_CS, // CS pin
.queue_size = SPI_TRANSACTION_COUNT, // We want to be able to queue 5 transactions at a time
.pre_cb = &spi_pre_transfer_cb, // Specify pre-transfer callback to handle D/C line and SPI lock
.flags = SPI_DEVICE_NO_DUMMY, // SPI_DEVICE_HALFDUPLEX;
};

esp_err_t ret;

// Initialize the SPI bus
ret = spi_bus_initialize(RG_SCREEN_HOST, &buscfg, SPI_DMA_CH_AUTO);
RG_ASSERT(ret == ESP_OK || ret == ESP_ERR_INVALID_STATE, "spi_bus_initialize failed.");

ret = spi_bus_add_device(RG_SCREEN_HOST, &devcfg, &spi_dev);
RG_ASSERT(ret == ESP_OK, "spi_bus_add_device failed.");

rg_task_create("rg_spi", &spi_task, NULL, 1.5 * 1024, RG_TASK_PRIORITY_7, 1);
}

static void spi_deinit(void)
{
spi_bus_remove_device(spi_dev);
spi_bus_free(RG_SCREEN_HOST);
}

#define ILI9341_CMD(cmd, data...) \
{ \
const uint8_t c = cmd, x[] = {data}; \
spi_queue_transaction(&c, 1, 0); \
if (sizeof(x)) \
spi_queue_transaction(&x, sizeof(x), 1); \
}

static void lcd_set_backlight(float percent)
{
float level = RG_MIN(RG_MAX(percent / 100.f, 0), 1.f);
int error_code = 0;

#if defined(RG_GPIO_LCD_BCKL)
error_code = ledc_set_fade_time_and_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0x1FFF * level, 50, 0);
#elif defined(RG_TARGET_QTPY_GAMER)
rg_i2c_gpio_set_direction(AW_TFT_BACKLIGHT, 0);
rg_i2c_gpio_set_level(AW_TFT_BACKLIGHT, level * 255);
#endif

if (error_code)
RG_LOGE("failed setting backlight to %d%% (0x%02X)\n", (int)(100 * level), error_code);
else
RG_LOGI("backlight set to %d%%\n", (int)(100 * level));
}

static void lcd_set_window(int left, int top, int width, int height)
{
int right = left + width - 1;
int bottom = top + height - 1;

if (left < 0 || top < 0 || right >= display.screen.real_width || bottom >= display.screen.real_height)
RG_LOGW("Bad lcd window (x0=%d, y0=%d, x1=%d, y1=%d)\n", left, top, right, bottom);

ILI9341_CMD(0x2A, left >> 8, left & 0xff, right >> 8, right & 0xff); // Horiz
ILI9341_CMD(0x2B, top >> 8, top & 0xff, bottom >> 8, bottom & 0xff); // Vert
ILI9341_CMD(0x2C); // Memory write
}

static inline uint16_t *lcd_get_buffer(size_t length)
{
// RG_ASSERT(length < LCD_BUFFER_LENGTH, "Invalid length");
return spi_take_buffer();
}

static inline void lcd_send_buffer(uint16_t *buffer, size_t length)
{
if (length > 0)
spi_queue_transaction(buffer, length * sizeof(*buffer), 3);
else
spi_give_buffer(buffer);
}

static void lcd_sync(void)
{
// Unused for SPI LCD
}

static void lcd_init(void)
{
#ifdef RG_GPIO_LCD_BCKL
// Initialize backlight at 0% to avoid the lcd reset flash
ledc_timer_config(&(ledc_timer_config_t){
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
});
ledc_channel_config(&(ledc_channel_config_t){
.channel = LEDC_CHANNEL_0,
.duty = 0,
.gpio_num = RG_GPIO_LCD_BCKL,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_sel = LEDC_TIMER_0,
});
ledc_fade_func_install(0);
#endif

spi_init();

// Setup Data/Command line
gpio_set_direction(RG_GPIO_LCD_DC, GPIO_MODE_OUTPUT);
gpio_set_level(RG_GPIO_LCD_DC, 1);

#if defined(RG_GPIO_LCD_RST)
gpio_set_direction(RG_GPIO_LCD_RST, GPIO_MODE_OUTPUT);
gpio_set_level(RG_GPIO_LCD_RST, 0);
rg_usleep(100 * 1000);
gpio_set_level(RG_GPIO_LCD_RST, 1);
rg_usleep(10 * 1000);
#elif defined(RG_TARGET_QTPY_GAMER)
rg_i2c_gpio_set_direction(AW_TFT_RESET, 0);
rg_i2c_gpio_set_level(AW_TFT_RESET, 0);
rg_usleep(100 * 1000);
rg_i2c_gpio_set_level(AW_TFT_RESET, 1);
rg_usleep(10 * 1000);
#endif

ILI9341_CMD(0x01); // Reset
rg_usleep(5 * 1000); // Wait 5ms after reset
ILI9341_CMD(0x3A, 0X05); // Pixel Format Set RGB565
#ifdef RG_SCREEN_INIT
RG_SCREEN_INIT();
#else
#warning "LCD init sequence is not defined for this device!"
#endif
ILI9341_CMD(0x11); // Exit Sleep
rg_usleep(5 * 1000);// Wait 5ms after sleep out
ILI9341_CMD(0x29); // Display on

rg_display_clear(C_BLACK);
rg_usleep(10 * 1000);
lcd_set_backlight(config.backlight);
}

static void lcd_deinit(void)
{
#ifdef RG_SCREEN_DEINIT
RG_SCREEN_DEINIT();
#endif
spi_deinit();
// gpio_reset_pin(RG_GPIO_LCD_BCKL);
// gpio_reset_pin(RG_GPIO_LCD_DC);
}

const rg_display_driver_t rg_display_driver_ili9341 = {
.name = "ili9341",
};
30 changes: 30 additions & 0 deletions components/retro-go/drivers/display/sdl2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <SDL2/SDL.h>

static void lcd_init(void)
{
}

static void lcd_deinit(void)
{
}

static void lcd_set_backlight(float percent)
{
}

static inline uint16_t *lcd_get_buffer(size_t length)
{
return (void *)0;
}

static inline void lcd_send_buffer(uint16_t *buffer, size_t length)
{
}

static void lcd_sync(void)
{
}

const rg_display_driver_t rg_display_driver_sdl2 = {
.name = "sdl2",
};
Loading

0 comments on commit ac46ccb

Please sign in to comment.