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:
parent
c809c3730d
commit
a68c1aa4ad
3 changed files with 189 additions and 17 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
* Copyright 2024 TiaC Systems
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -9,6 +10,7 @@
|
|||
#include <zephyr/drivers/mipi_dbi.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
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;
|
||||
/* Reset GPIO */
|
||||
const struct gpio_dt_spec reset;
|
||||
/* Minimum transfer bits */
|
||||
const uint8_t xfr_min_bits;
|
||||
};
|
||||
|
||||
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
|
||||
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
|
||||
* (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
|
||||
|
@ -93,8 +109,10 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
|
||||
|
||||
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,
|
||||
bool cmd_present, uint8_t cmd,
|
||||
const uint8_t *data_buf, size_t len)
|
||||
|
@ -140,11 +158,104 @@ out:
|
|||
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,
|
||||
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 mipi_dbi_spi_data *data = dev->data;
|
||||
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,
|
||||
cmd_present, cmd,
|
||||
data_buf, len);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
} else if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
|
||||
ret = mipi_dbi_spi_write_helper_4wire(dev, dbi_config,
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
#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:
|
||||
k_mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
|
@ -433,6 +560,7 @@ static DEVICE_API(mipi_dbi, mipi_dbi_spi_driver_api) = {
|
|||
DT_INST_PHANDLE(n, spi_dev)), \
|
||||
.cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_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; \
|
||||
\
|
||||
|
|
|
@ -27,6 +27,22 @@ properties:
|
|||
description: |
|
||||
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:
|
||||
type: boolean
|
||||
description: |
|
||||
|
|
|
@ -110,6 +110,34 @@
|
|||
#define MIPI_DBI_MODE_8080_BUS_9_BIT 0x7
|
||||
#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
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue