drivers: mipi_dbi_spi: add 16-bit transfer to C4

Extends the MIPI DBI SPI driver class for operating mode C4, SPI 4-wire,
with 16 write clocks to send one or multiple byte for commands. Generic
data (e.g. GRAM) aligned to 16-bit are passed through and stuffed with
bytes if required.

Signed-off-by: Stephan Linz <linz@li-pro.net>
This commit is contained in:
Stephan Linz 2024-11-29 23:59:59 +01:00 committed by Benjamin Cabé
commit a68c1aa4ad
3 changed files with 189 additions and 17 deletions

View file

@ -1,5 +1,6 @@
/* /*
* Copyright 2023 NXP * Copyright 2023 NXP
* Copyright 2024 TiaC Systems
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -9,6 +10,7 @@
#include <zephyr/drivers/mipi_dbi.h> #include <zephyr/drivers/mipi_dbi.h>
#include <zephyr/drivers/spi.h> #include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h> #include <zephyr/drivers/gpio.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mipi_dbi_spi, CONFIG_MIPI_DBI_LOG_LEVEL); LOG_MODULE_REGISTER(mipi_dbi_spi, CONFIG_MIPI_DBI_LOG_LEVEL);
@ -20,6 +22,8 @@ struct mipi_dbi_spi_config {
const struct gpio_dt_spec cmd_data; const struct gpio_dt_spec cmd_data;
/* Reset GPIO */ /* Reset GPIO */
const struct gpio_dt_spec reset; const struct gpio_dt_spec reset;
/* Minimum transfer bits */
const uint8_t xfr_min_bits;
}; };
struct mipi_dbi_spi_data { struct mipi_dbi_spi_data {
@ -38,6 +42,18 @@ struct mipi_dbi_spi_data {
#define MIPI_DBI_SPI_READ_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_WRITE_ONLY_ABSENT) 0 #define MIPI_DBI_SPI_READ_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_WRITE_ONLY_ABSENT) 0
uint32_t var = MIPI_DBI_SPI_READ_REQUIRED; uint32_t var = MIPI_DBI_SPI_READ_REQUIRED;
/* Expands to 1 if the node does reflect the enum in `xfr-min-bits` property */
#define _XFR_8BITS(n) (DT_INST_PROP(n, xfr_min_bits) == MIPI_DBI_SPI_XFR_8BIT) |
#define _XFR_16BITS(n) (DT_INST_PROP(n, xfr_min_bits) == MIPI_DBI_SPI_XFR_16BIT) |
/* This macros will evaluate to 1 if any of the nodes with zephyr,mipi-dbi-spi
* have the `xfr-min-bits` property to corresponding enum value. The intention
* here is to allow the write helper functions to be optimized out when not all
* minimum transfer bits will be needed.
*/
#define MIPI_DBI_SPI_WRITE_8BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_XFR_8BITS) 0
#define MIPI_DBI_SPI_WRITE_16BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_XFR_16BITS) 0
/* In Type C mode 1 MIPI BIT communication, the 9th bit of the word /* In Type C mode 1 MIPI BIT communication, the 9th bit of the word
* (first bit sent in each word) indicates if the word is a command or * (first bit sent in each word) indicates if the word is a command or
* data. Typically 0 indicates a command and 1 indicates data, but some * data. Typically 0 indicates a command and 1 indicates data, but some
@ -93,11 +109,13 @@ out:
return ret; return ret;
} }
#if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
static inline int static inline int
mipi_dbi_spi_write_helper_4wire(const struct device *dev, mipi_dbi_spi_write_helper_4wire_8bit(const struct device *dev,
const struct mipi_dbi_config *dbi_config, const struct mipi_dbi_config *dbi_config,
bool cmd_present, uint8_t cmd, bool cmd_present, uint8_t cmd,
const uint8_t *data_buf, size_t len) const uint8_t *data_buf, size_t len)
{ {
const struct mipi_dbi_spi_config *config = dev->config; const struct mipi_dbi_spi_config *config = dev->config;
struct spi_buf buffer; struct spi_buf buffer;
@ -140,11 +158,104 @@ out:
return ret; return ret;
} }
#endif /* MIPI_DBI_SPI_WRITE_8BIT_REQUIRED */
#if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
static inline int
mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev,
const struct mipi_dbi_config *dbi_config,
bool cmd_present, uint8_t cmd,
const uint8_t *data_buf, size_t len)
{
const struct mipi_dbi_spi_config *config = dev->config;
struct spi_buf buffer;
struct spi_buf_set buf_set = {
.buffers = &buffer,
.count = 1,
};
uint16_t data16;
int ret = 0;
/*
* 4 wire mode with toggle the command/data GPIO
* to indicate if we are sending a command or data
* but send 16-bit blocks (with bit stuffing).
*/
if (cmd_present) {
data16 = sys_cpu_to_be16(cmd);
buffer.buf = &data16;
buffer.len = sizeof(data16);
/* Set CD pin low for command */
gpio_pin_set_dt(&config->cmd_data, 0);
ret = spi_write(config->spi_dev, &dbi_config->config,
&buf_set);
if (ret < 0) {
goto out;
}
/* Set CD pin high for data, if there are any */
if (len > 0) {
gpio_pin_set_dt(&config->cmd_data, 1);
}
/* iterate command data */
for (int i = 0; i < len; i++) {
data16 = sys_cpu_to_be16(data_buf[i]);
ret = spi_write(config->spi_dev, &dbi_config->config,
&buf_set);
if (ret < 0) {
goto out;
}
}
} else {
int stuffing = len % sizeof(data16);
/* Set CD pin high for data, if there are any */
if (len > 0) {
gpio_pin_set_dt(&config->cmd_data, 1);
}
/* pass through generic device data */
if (len - stuffing > 0) {
buffer.buf = (void *)data_buf;
buffer.len = len - stuffing;
ret = spi_write(config->spi_dev, &dbi_config->config,
&buf_set);
if (ret < 0) {
goto out;
}
}
/* iterate remaining data with stuffing */
for (int i = len - stuffing; i < len; i++) {
data16 = sys_cpu_to_be16(data_buf[i]);
buffer.buf = &data16;
buffer.len = sizeof(data16);
ret = spi_write(config->spi_dev, &dbi_config->config,
&buf_set);
if (ret < 0) {
goto out;
}
}
}
out:
return ret;
}
#endif /* MIPI_DBI_SPI_WRITE_16BIT_REQUIRED */
static int mipi_dbi_spi_write_helper(const struct device *dev, static int mipi_dbi_spi_write_helper(const struct device *dev,
const struct mipi_dbi_config *dbi_config, const struct mipi_dbi_config *dbi_config,
bool cmd_present, uint8_t cmd, bool cmd_present, uint8_t cmd,
const uint8_t *data_buf, size_t len) const uint8_t *data_buf, size_t len)
{ {
const struct mipi_dbi_spi_config *config = dev->config;
struct mipi_dbi_spi_data *data = dev->data; struct mipi_dbi_spi_data *data = dev->data;
int ret = 0; int ret = 0;
@ -158,20 +269,36 @@ static int mipi_dbi_spi_write_helper(const struct device *dev,
ret = mipi_dbi_spi_write_helper_3wire(dev, dbi_config, ret = mipi_dbi_spi_write_helper_3wire(dev, dbi_config,
cmd_present, cmd, cmd_present, cmd,
data_buf, len); data_buf, len);
if (ret < 0) { goto out;
goto out;
}
} else if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
ret = mipi_dbi_spi_write_helper_4wire(dev, dbi_config,
cmd_present, cmd,
data_buf, len);
if (ret < 0) {
goto out;
}
} else {
/* Otherwise, unsupported mode */
ret = -ENOTSUP;
} }
if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
#if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_8BIT) {
ret = mipi_dbi_spi_write_helper_4wire_8bit(
dev, dbi_config,
cmd_present, cmd,
data_buf, len);
goto out;
}
#endif
#if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_16BIT) {
ret = mipi_dbi_spi_write_helper_4wire_16bit(
dev, dbi_config,
cmd_present, cmd,
data_buf, len);
goto out;
}
#endif
}
/* Otherwise, unsupported mode */
ret = -ENOTSUP;
out: out:
k_mutex_unlock(&data->lock); k_mutex_unlock(&data->lock);
return ret; return ret;
@ -433,6 +560,7 @@ static DEVICE_API(mipi_dbi, mipi_dbi_spi_driver_api) = {
DT_INST_PHANDLE(n, spi_dev)), \ DT_INST_PHANDLE(n, spi_dev)), \
.cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}), \ .cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}), \
.reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \ .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
.xfr_min_bits = DT_INST_PROP(n, xfr_min_bits) \
}; \ }; \
static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n; \ static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n; \
\ \

View file

@ -27,6 +27,22 @@ properties:
description: | description: |
Reset GPIO pin. Set high to reset the display Reset GPIO pin. Set high to reset the display
xfr-min-bits:
type: int
default: 8
description:
On rare types of SPI interfaces, discrete shift registers can be found
whose task is to convert the serial SPI bit stream to the parallel MCU
interface with clock and bit accuracy. Typically, these are 16 bits wide.
Use the macros, not the actual enum value. Here is the concordance list
(see dt-bindings/mipi_dbi/mipi_dbi.h)
8 MIPI_DBI_SPI_XFR_8BIT
16 MIPI_DBI_SPI_XFR_16BIT
enum:
- 8
- 16
write-only: write-only:
type: boolean type: boolean
description: | description: |

View file

@ -110,6 +110,34 @@
#define MIPI_DBI_MODE_8080_BUS_9_BIT 0x7 #define MIPI_DBI_MODE_8080_BUS_9_BIT 0x7
#define MIPI_DBI_MODE_8080_BUS_8_BIT 0x8 #define MIPI_DBI_MODE_8080_BUS_8_BIT 0x8
/**
* SPI transfer of DBI commands as 8-bit blocks, the default behaviour in
* SPI 4 wire (Type C3) mode. The clocking diagram corresponds exactly to
* the illustration of Type C3.
*/
#define MIPI_DBI_SPI_XFR_8BIT 8
/**
* SPI transfer of DBI commands as 16-bit blocks, a rare and seldom behaviour
* in SPI 4 wire (Type C3) mode. The corresponding clocking diagram is slightly
* different to the illustration of Type C3.
*
* .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-.
* SCK -' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '---
*
* -.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.-
* DOUT |D15|D14|D13|D12|D11|D10| D9| D8| D7| D6| D5| D4| D3| D2| D1| D0|
* -'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'-
* | Word 1 (stuffing) : (byte) |
*
* -. .-
* CS '---------------------------------------------------------------'
*
* -.---------------------------------------------------------------.-
* CD | D/C |
* -'---------------------------------------------------------------'-
*/
#define MIPI_DBI_SPI_XFR_16BIT 16
/** /**
* @} * @}
*/ */