Merge branch 'contrib/github_pr_8496' into 'master'

SPI  LCD support large color transfers (GitHub PR)

Closes IDFGH-6874

See merge request espressif/esp-idf!17391
This commit is contained in:
morris
2022-03-14 22:22:00 +08:00
9 changed files with 79 additions and 47 deletions

View File

@@ -15,12 +15,15 @@
#endif
#include "esp_lcd_panel_io_interface.h"
#include "esp_lcd_panel_io.h"
#include "hal/spi_ll.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_lcd_common.h"
#define LCD_SPI_MAX_DATA_SIZE (SPI_LL_DATA_MAX_BIT_LEN / 8)
static const char *TAG = "lcd_panel.io.spi";
static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, const void *param, size_t param_size);
@@ -33,7 +36,7 @@ typedef struct {
spi_transaction_t base;
struct {
unsigned int dc_gpio_level: 1;
unsigned int trans_is_color: 1;
unsigned int en_trans_done_cb: 1;
} flags;
} lcd_spi_trans_descriptor_t;
@@ -250,18 +253,53 @@ static esp_err_t panel_io_spi_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base);
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed");
// sending LCD color data
lcd_trans->flags.trans_is_color = 1;
lcd_trans->flags.dc_gpio_level = spi_panel_io->flags.dc_data_level; // set D/C line to data mode
lcd_trans->base.length = color_size * 8; // transaction length is in bits
lcd_trans->base.tx_buffer = color;
if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
lcd_trans->base.cmd = spi_panel_io->flags.dc_data_level;
}
// color data is usually large, using queue+blocking mode
ret = spi_device_queue_trans(spi_panel_io->spi_dev, &lcd_trans->base, portMAX_DELAY);
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) color failed");
spi_panel_io->num_trans_inflight++;
// split to chunks if required:
// the SPI bus has a maximum transaction size determined by SPI_USR_MOSI_DBITLEN's bit width
do {
size_t chunk_size = color_size;
if (spi_panel_io->num_trans_inflight < spi_panel_io->queue_size) {
// get the next available transaction
lcd_trans = &spi_panel_io->trans_pool[spi_panel_io->num_trans_inflight];
} else {
// transaction pool has used up, recycle one transaction
ret = spi_device_get_trans_result(spi_panel_io->spi_dev, &spi_trans, portMAX_DELAY);
ESP_GOTO_ON_ERROR(ret, err, TAG, "recycle spi transactions failed");
lcd_trans = __containerof(spi_trans, lcd_spi_trans_descriptor_t, base);
spi_panel_io->num_trans_inflight--;
}
memset(lcd_trans, 0, sizeof(lcd_spi_trans_descriptor_t));
// SPI per-transfer size has its limitation, if the color buffer is too big, we need to split it into multiple trunks
if (chunk_size > LCD_SPI_MAX_DATA_SIZE) {
// cap the transfer size to the maximum supported by the bus
chunk_size = LCD_SPI_MAX_DATA_SIZE;
} else {
// mark en_trans_done_cb only at the last round to avoid premature completion callback
lcd_trans->flags.en_trans_done_cb = 1;
}
lcd_trans->base.user = spi_panel_io;
lcd_trans->flags.dc_gpio_level = spi_panel_io->flags.dc_data_level; // set D/C line to data mode
lcd_trans->base.length = chunk_size * 8; // transaction length is in bits
lcd_trans->base.tx_buffer = color;
if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
lcd_trans->base.cmd = spi_panel_io->flags.dc_data_level;
}
if (spi_panel_io->flags.octal_mode) {
// use 8 lines for transmitting command, address and data
lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT);
}
// color data is usually large, using queue+blocking mode
ret = spi_device_queue_trans(spi_panel_io->spi_dev, &lcd_trans->base, portMAX_DELAY);
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) color failed");
spi_panel_io->num_trans_inflight++;
// move on to the next chunk
color = (const uint8_t *)color + chunk_size;
color_size -= chunk_size;
} while (color_size > 0); // continue while we have remaining data to transmit
err:
return ret;
@@ -280,7 +318,7 @@ static void lcd_spi_post_trans_color_cb(spi_transaction_t *trans)
{
esp_lcd_panel_io_spi_t *spi_panel_io = trans->user;
lcd_spi_trans_descriptor_t *lcd_trans = __containerof(trans, lcd_spi_trans_descriptor_t, base);
if (lcd_trans->flags.trans_is_color) {
if (lcd_trans->flags.en_trans_done_cb) {
if (spi_panel_io->on_color_trans_done) {
spi_panel_io->on_color_trans_done(&spi_panel_io->base, NULL, spi_panel_io->user_ctx);
}