Skip to content

Commit a68c1aa

Browse files
rexutkartben
authored andcommitted
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>
1 parent c809c37 commit a68c1aa

File tree

3 files changed

+185
-13
lines changed

3 files changed

+185
-13
lines changed

drivers/mipi_dbi/mipi_dbi_spi.c

Lines changed: 141 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright 2023 NXP
3+
* Copyright 2024 TiaC Systems
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
@@ -9,6 +10,7 @@
910
#include <zephyr/drivers/mipi_dbi.h>
1011
#include <zephyr/drivers/spi.h>
1112
#include <zephyr/drivers/gpio.h>
13+
#include <zephyr/sys/byteorder.h>
1214

1315
#include <zephyr/logging/log.h>
1416
LOG_MODULE_REGISTER(mipi_dbi_spi, CONFIG_MIPI_DBI_LOG_LEVEL);
@@ -20,6 +22,8 @@ struct mipi_dbi_spi_config {
2022
const struct gpio_dt_spec cmd_data;
2123
/* Reset GPIO */
2224
const struct gpio_dt_spec reset;
25+
/* Minimum transfer bits */
26+
const uint8_t xfr_min_bits;
2327
};
2428

2529
struct mipi_dbi_spi_data {
@@ -38,6 +42,18 @@ struct mipi_dbi_spi_data {
3842
#define MIPI_DBI_SPI_READ_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_WRITE_ONLY_ABSENT) 0
3943
uint32_t var = MIPI_DBI_SPI_READ_REQUIRED;
4044

45+
/* Expands to 1 if the node does reflect the enum in `xfr-min-bits` property */
46+
#define _XFR_8BITS(n) (DT_INST_PROP(n, xfr_min_bits) == MIPI_DBI_SPI_XFR_8BIT) |
47+
#define _XFR_16BITS(n) (DT_INST_PROP(n, xfr_min_bits) == MIPI_DBI_SPI_XFR_16BIT) |
48+
49+
/* This macros will evaluate to 1 if any of the nodes with zephyr,mipi-dbi-spi
50+
* have the `xfr-min-bits` property to corresponding enum value. The intention
51+
* here is to allow the write helper functions to be optimized out when not all
52+
* minimum transfer bits will be needed.
53+
*/
54+
#define MIPI_DBI_SPI_WRITE_8BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_XFR_8BITS) 0
55+
#define MIPI_DBI_SPI_WRITE_16BIT_REQUIRED DT_INST_FOREACH_STATUS_OKAY(_XFR_16BITS) 0
56+
4157
/* In Type C mode 1 MIPI BIT communication, the 9th bit of the word
4258
* (first bit sent in each word) indicates if the word is a command or
4359
* data. Typically 0 indicates a command and 1 indicates data, but some
@@ -93,11 +109,13 @@ mipi_dbi_spi_write_helper_3wire(const struct device *dev,
93109
return ret;
94110
}
95111

112+
#if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
113+
96114
static inline int
97-
mipi_dbi_spi_write_helper_4wire(const struct device *dev,
98-
const struct mipi_dbi_config *dbi_config,
99-
bool cmd_present, uint8_t cmd,
100-
const uint8_t *data_buf, size_t len)
115+
mipi_dbi_spi_write_helper_4wire_8bit(const struct device *dev,
116+
const struct mipi_dbi_config *dbi_config,
117+
bool cmd_present, uint8_t cmd,
118+
const uint8_t *data_buf, size_t len)
101119
{
102120
const struct mipi_dbi_spi_config *config = dev->config;
103121
struct spi_buf buffer;
@@ -140,11 +158,104 @@ mipi_dbi_spi_write_helper_4wire(const struct device *dev,
140158
return ret;
141159
}
142160

161+
#endif /* MIPI_DBI_SPI_WRITE_8BIT_REQUIRED */
162+
163+
#if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
164+
165+
static inline int
166+
mipi_dbi_spi_write_helper_4wire_16bit(const struct device *dev,
167+
const struct mipi_dbi_config *dbi_config,
168+
bool cmd_present, uint8_t cmd,
169+
const uint8_t *data_buf, size_t len)
170+
{
171+
const struct mipi_dbi_spi_config *config = dev->config;
172+
struct spi_buf buffer;
173+
struct spi_buf_set buf_set = {
174+
.buffers = &buffer,
175+
.count = 1,
176+
};
177+
uint16_t data16;
178+
int ret = 0;
179+
180+
/*
181+
* 4 wire mode with toggle the command/data GPIO
182+
* to indicate if we are sending a command or data
183+
* but send 16-bit blocks (with bit stuffing).
184+
*/
185+
186+
if (cmd_present) {
187+
data16 = sys_cpu_to_be16(cmd);
188+
buffer.buf = &data16;
189+
buffer.len = sizeof(data16);
190+
191+
/* Set CD pin low for command */
192+
gpio_pin_set_dt(&config->cmd_data, 0);
193+
ret = spi_write(config->spi_dev, &dbi_config->config,
194+
&buf_set);
195+
if (ret < 0) {
196+
goto out;
197+
}
198+
199+
/* Set CD pin high for data, if there are any */
200+
if (len > 0) {
201+
gpio_pin_set_dt(&config->cmd_data, 1);
202+
}
203+
204+
/* iterate command data */
205+
for (int i = 0; i < len; i++) {
206+
data16 = sys_cpu_to_be16(data_buf[i]);
207+
208+
ret = spi_write(config->spi_dev, &dbi_config->config,
209+
&buf_set);
210+
if (ret < 0) {
211+
goto out;
212+
}
213+
}
214+
} else {
215+
int stuffing = len % sizeof(data16);
216+
217+
/* Set CD pin high for data, if there are any */
218+
if (len > 0) {
219+
gpio_pin_set_dt(&config->cmd_data, 1);
220+
}
221+
222+
/* pass through generic device data */
223+
if (len - stuffing > 0) {
224+
buffer.buf = (void *)data_buf;
225+
buffer.len = len - stuffing;
226+
227+
ret = spi_write(config->spi_dev, &dbi_config->config,
228+
&buf_set);
229+
if (ret < 0) {
230+
goto out;
231+
}
232+
}
233+
234+
/* iterate remaining data with stuffing */
235+
for (int i = len - stuffing; i < len; i++) {
236+
data16 = sys_cpu_to_be16(data_buf[i]);
237+
buffer.buf = &data16;
238+
buffer.len = sizeof(data16);
239+
240+
ret = spi_write(config->spi_dev, &dbi_config->config,
241+
&buf_set);
242+
if (ret < 0) {
243+
goto out;
244+
}
245+
}
246+
}
247+
out:
248+
return ret;
249+
}
250+
251+
#endif /* MIPI_DBI_SPI_WRITE_16BIT_REQUIRED */
252+
143253
static int mipi_dbi_spi_write_helper(const struct device *dev,
144254
const struct mipi_dbi_config *dbi_config,
145255
bool cmd_present, uint8_t cmd,
146256
const uint8_t *data_buf, size_t len)
147257
{
258+
const struct mipi_dbi_spi_config *config = dev->config;
148259
struct mipi_dbi_spi_data *data = dev->data;
149260
int ret = 0;
150261

@@ -158,20 +269,36 @@ static int mipi_dbi_spi_write_helper(const struct device *dev,
158269
ret = mipi_dbi_spi_write_helper_3wire(dev, dbi_config,
159270
cmd_present, cmd,
160271
data_buf, len);
161-
if (ret < 0) {
272+
goto out;
273+
}
274+
275+
if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
276+
277+
#if MIPI_DBI_SPI_WRITE_8BIT_REQUIRED
278+
if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_8BIT) {
279+
ret = mipi_dbi_spi_write_helper_4wire_8bit(
280+
dev, dbi_config,
281+
cmd_present, cmd,
282+
data_buf, len);
162283
goto out;
163284
}
164-
} else if (dbi_config->mode == MIPI_DBI_MODE_SPI_4WIRE) {
165-
ret = mipi_dbi_spi_write_helper_4wire(dev, dbi_config,
166-
cmd_present, cmd,
167-
data_buf, len);
168-
if (ret < 0) {
285+
#endif
286+
287+
#if MIPI_DBI_SPI_WRITE_16BIT_REQUIRED
288+
if (config->xfr_min_bits == MIPI_DBI_SPI_XFR_16BIT) {
289+
ret = mipi_dbi_spi_write_helper_4wire_16bit(
290+
dev, dbi_config,
291+
cmd_present, cmd,
292+
data_buf, len);
169293
goto out;
170294
}
171-
} else {
172-
/* Otherwise, unsupported mode */
173-
ret = -ENOTSUP;
295+
#endif
296+
174297
}
298+
299+
/* Otherwise, unsupported mode */
300+
ret = -ENOTSUP;
301+
175302
out:
176303
k_mutex_unlock(&data->lock);
177304
return ret;
@@ -433,6 +560,7 @@ static DEVICE_API(mipi_dbi, mipi_dbi_spi_driver_api) = {
433560
DT_INST_PHANDLE(n, spi_dev)), \
434561
.cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}), \
435562
.reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
563+
.xfr_min_bits = DT_INST_PROP(n, xfr_min_bits) \
436564
}; \
437565
static struct mipi_dbi_spi_data mipi_dbi_spi_data_##n; \
438566
\

dts/bindings/mipi-dbi/zephyr,mipi-dbi-spi.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ properties:
2727
description: |
2828
Reset GPIO pin. Set high to reset the display
2929
30+
xfr-min-bits:
31+
type: int
32+
default: 8
33+
description:
34+
On rare types of SPI interfaces, discrete shift registers can be found
35+
whose task is to convert the serial SPI bit stream to the parallel MCU
36+
interface with clock and bit accuracy. Typically, these are 16 bits wide.
37+
38+
Use the macros, not the actual enum value. Here is the concordance list
39+
(see dt-bindings/mipi_dbi/mipi_dbi.h)
40+
8 MIPI_DBI_SPI_XFR_8BIT
41+
16 MIPI_DBI_SPI_XFR_16BIT
42+
enum:
43+
- 8
44+
- 16
45+
3046
write-only:
3147
type: boolean
3248
description: |

include/zephyr/dt-bindings/mipi_dbi/mipi_dbi.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,34 @@
110110
#define MIPI_DBI_MODE_8080_BUS_9_BIT 0x7
111111
#define MIPI_DBI_MODE_8080_BUS_8_BIT 0x8
112112

113+
/**
114+
* SPI transfer of DBI commands as 8-bit blocks, the default behaviour in
115+
* SPI 4 wire (Type C3) mode. The clocking diagram corresponds exactly to
116+
* the illustration of Type C3.
117+
*/
118+
#define MIPI_DBI_SPI_XFR_8BIT 8
119+
/**
120+
* SPI transfer of DBI commands as 16-bit blocks, a rare and seldom behaviour
121+
* in SPI 4 wire (Type C3) mode. The corresponding clocking diagram is slightly
122+
* different to the illustration of Type C3.
123+
*
124+
* .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-. .-.
125+
* SCK -' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '-' '---
126+
*
127+
* -.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.-
128+
* DOUT |D15|D14|D13|D12|D11|D10| D9| D8| D7| D6| D5| D4| D3| D2| D1| D0|
129+
* -'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'-
130+
* | Word 1 (stuffing) : (byte) |
131+
*
132+
* -. .-
133+
* CS '---------------------------------------------------------------'
134+
*
135+
* -.---------------------------------------------------------------.-
136+
* CD | D/C |
137+
* -'---------------------------------------------------------------'-
138+
*/
139+
#define MIPI_DBI_SPI_XFR_16BIT 16
140+
113141
/**
114142
* @}
115143
*/

0 commit comments

Comments
 (0)