1 /*
2 * MIPI DBI Type A and B driver using GPIO
3 *
4 * Copyright 2024 Stefan Gloor
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #define DT_DRV_COMPAT zephyr_mipi_dbi_bitbang
10
11 #include <zephyr/drivers/mipi_dbi.h>
12 #include <zephyr/drivers/gpio.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(mipi_dbi_bitbang, CONFIG_MIPI_DBI_LOG_LEVEL);
16
17 /* The MIPI DBI spec allows 8, 9, and 16 bits */
18 #define MIPI_DBI_MAX_DATA_BUS_WIDTH 16
19
20 /* Compile in a data bus LUT for improved performance if at least one instance uses an 8-bit bus */
21 #define _8_BIT_MODE_PRESENT(n) (DT_INST_PROP_LEN(n, data_gpios) == 8) |
22 #define MIPI_DBI_8_BIT_MODE DT_INST_FOREACH_STATUS_OKAY(_8_BIT_MODE_PRESENT) 0
23
24 struct mipi_dbi_bitbang_config {
25 /* Parallel 8080/6800 data GPIOs */
26 const struct gpio_dt_spec data[MIPI_DBI_MAX_DATA_BUS_WIDTH];
27 const uint8_t data_bus_width;
28
29 /* Read (type B) GPIO */
30 const struct gpio_dt_spec rd;
31
32 /* Write (type B) or Read/!Write (type A) GPIO */
33 const struct gpio_dt_spec wr;
34
35 /* Enable/strobe GPIO (type A) */
36 const struct gpio_dt_spec e;
37
38 /* Chip-select GPIO */
39 const struct gpio_dt_spec cs;
40
41 /* Command/Data GPIO */
42 const struct gpio_dt_spec cmd_data;
43
44 /* Reset GPIO */
45 const struct gpio_dt_spec reset;
46
47 #if MIPI_DBI_8_BIT_MODE
48 /* Data GPIO remap look-up table. Valid if mipi_dbi_bitbang_data.single_port is set */
49 const uint32_t data_lut[256];
50
51 /* Mask of all data pins. Valid if mipi_dbi_bitbang_data.single_port is set */
52 const uint32_t data_mask;
53 #endif
54 };
55
56 struct mipi_dbi_bitbang_data {
57 struct k_mutex lock;
58
59 #if MIPI_DBI_8_BIT_MODE
60 /* Indicates whether all data GPIO pins are on the same port and the data LUT is used. */
61 bool single_port;
62
63 /* Data GPIO port device. Valid if mipi_dbi_bitbang_data.single_port is set */
64 const struct device *data_port;
65 #endif
66 };
67
mipi_dbi_bitbang_set_data_gpios(const struct mipi_dbi_bitbang_config * config,struct mipi_dbi_bitbang_data * data,uint32_t value)68 static inline void mipi_dbi_bitbang_set_data_gpios(const struct mipi_dbi_bitbang_config *config,
69 struct mipi_dbi_bitbang_data *data,
70 uint32_t value)
71 {
72 #if MIPI_DBI_8_BIT_MODE
73 if (data->single_port) {
74 gpio_port_set_masked(data->data_port, config->data_mask, config->data_lut[value]);
75 } else {
76 #endif
77 for (int i = 0; i < config->data_bus_width; i++) {
78 gpio_pin_set_dt(&config->data[i], (value & (1 << i)) != 0);
79 }
80 #if MIPI_DBI_8_BIT_MODE
81 }
82 #endif
83 }
84
mipi_dbi_bitbang_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)85 static int mipi_dbi_bitbang_write_helper(const struct device *dev,
86 const struct mipi_dbi_config *dbi_config, bool cmd_present,
87 uint8_t cmd, const uint8_t *data_buf, size_t len)
88 {
89 const struct mipi_dbi_bitbang_config *config = dev->config;
90 struct mipi_dbi_bitbang_data *data = dev->data;
91 int ret = 0;
92 uint8_t value;
93
94 ret = k_mutex_lock(&data->lock, K_FOREVER);
95 if (ret < 0) {
96 return ret;
97 }
98
99 switch (dbi_config->mode) {
100 case MIPI_DBI_MODE_8080_BUS_8_BIT:
101 case MIPI_DBI_MODE_8080_BUS_9_BIT:
102 case MIPI_DBI_MODE_8080_BUS_16_BIT:
103 gpio_pin_set_dt(&config->cs, 1);
104 if (cmd_present) {
105 gpio_pin_set_dt(&config->wr, 0);
106 gpio_pin_set_dt(&config->cmd_data, 0);
107 mipi_dbi_bitbang_set_data_gpios(config, data, cmd);
108 gpio_pin_set_dt(&config->wr, 1);
109 }
110 if (len > 0) {
111 gpio_pin_set_dt(&config->cmd_data, 1);
112 while (len > 0) {
113 value = *(data_buf++);
114 gpio_pin_set_dt(&config->wr, 0);
115 mipi_dbi_bitbang_set_data_gpios(config, data, value);
116 gpio_pin_set_dt(&config->wr, 1);
117 len--;
118 }
119 }
120 gpio_pin_set_dt(&config->cs, 0);
121 break;
122
123 /* Clocked E mode */
124 case MIPI_DBI_MODE_6800_BUS_8_BIT:
125 case MIPI_DBI_MODE_6800_BUS_9_BIT:
126 case MIPI_DBI_MODE_6800_BUS_16_BIT:
127 gpio_pin_set_dt(&config->cs, 1);
128 gpio_pin_set_dt(&config->wr, 0);
129 if (cmd_present) {
130 gpio_pin_set_dt(&config->e, 1);
131 gpio_pin_set_dt(&config->cmd_data, 0);
132 mipi_dbi_bitbang_set_data_gpios(config, data, cmd);
133 gpio_pin_set_dt(&config->e, 0);
134 }
135 if (len > 0) {
136 gpio_pin_set_dt(&config->cmd_data, 1);
137 while (len > 0) {
138 value = *(data_buf++);
139 gpio_pin_set_dt(&config->e, 1);
140 mipi_dbi_bitbang_set_data_gpios(config, data, value);
141 gpio_pin_set_dt(&config->e, 0);
142 len--;
143 }
144 }
145 gpio_pin_set_dt(&config->cs, 0);
146 break;
147
148 default:
149 LOG_ERR("MIPI DBI mode %u is not supported.", dbi_config->mode);
150 ret = -ENOTSUP;
151 }
152
153 k_mutex_unlock(&data->lock);
154 return ret;
155 }
156
mipi_dbi_bitbang_command_write(const struct device * dev,const struct mipi_dbi_config * dbi_config,uint8_t cmd,const uint8_t * data_buf,size_t len)157 static int mipi_dbi_bitbang_command_write(const struct device *dev,
158 const struct mipi_dbi_config *dbi_config, uint8_t cmd,
159 const uint8_t *data_buf, size_t len)
160 {
161 return mipi_dbi_bitbang_write_helper(dev, dbi_config, true, cmd, data_buf, len);
162 }
163
mipi_dbi_bitbang_write_display(const struct device * dev,const struct mipi_dbi_config * dbi_config,const uint8_t * framebuf,struct display_buffer_descriptor * desc,enum display_pixel_format pixfmt)164 static int mipi_dbi_bitbang_write_display(const struct device *dev,
165 const struct mipi_dbi_config *dbi_config,
166 const uint8_t *framebuf,
167 struct display_buffer_descriptor *desc,
168 enum display_pixel_format pixfmt)
169 {
170 ARG_UNUSED(pixfmt);
171
172 return mipi_dbi_bitbang_write_helper(dev, dbi_config, false, 0x0, framebuf, desc->buf_size);
173 }
174
mipi_dbi_bitbang_reset(const struct device * dev,k_timeout_t delay)175 static int mipi_dbi_bitbang_reset(const struct device *dev, k_timeout_t delay)
176 {
177 const struct mipi_dbi_bitbang_config *config = dev->config;
178 int ret;
179
180 LOG_DBG("Performing hw reset.");
181
182 ret = gpio_pin_set_dt(&config->reset, 1);
183 if (ret < 0) {
184 return ret;
185 }
186 k_sleep(delay);
187 return gpio_pin_set_dt(&config->reset, 0);
188 }
189
mipi_dbi_bitbang_init(const struct device * dev)190 static int mipi_dbi_bitbang_init(const struct device *dev)
191 {
192 const struct mipi_dbi_bitbang_config *config = dev->config;
193 const char *failed_pin = NULL;
194 int ret = 0;
195 #if MIPI_DBI_8_BIT_MODE
196 struct mipi_dbi_bitbang_data *data = dev->data;
197 #endif
198
199 if (gpio_is_ready_dt(&config->cmd_data)) {
200 ret = gpio_pin_configure_dt(&config->cmd_data, GPIO_OUTPUT_ACTIVE);
201 if (ret < 0) {
202 failed_pin = "cmd_data";
203 goto fail;
204 }
205 gpio_pin_set_dt(&config->cmd_data, 0);
206 }
207 if (gpio_is_ready_dt(&config->rd)) {
208 gpio_pin_configure_dt(&config->rd, GPIO_OUTPUT_ACTIVE);
209 /* Don't emit an error because this pin is unused in type A */
210 gpio_pin_set_dt(&config->rd, 1);
211 }
212 if (gpio_is_ready_dt(&config->wr)) {
213 ret = gpio_pin_configure_dt(&config->wr, GPIO_OUTPUT_ACTIVE);
214 if (ret < 0) {
215 failed_pin = "wr";
216 goto fail;
217 }
218 gpio_pin_set_dt(&config->wr, 1);
219 }
220 if (gpio_is_ready_dt(&config->e)) {
221 gpio_pin_configure_dt(&config->e, GPIO_OUTPUT_ACTIVE);
222 /* Don't emit an error because this pin is unused in type B */
223 gpio_pin_set_dt(&config->e, 0);
224 }
225 if (gpio_is_ready_dt(&config->cs)) {
226 ret = gpio_pin_configure_dt(&config->cs, GPIO_OUTPUT_ACTIVE);
227 if (ret < 0) {
228 failed_pin = "cs";
229 goto fail;
230 }
231 gpio_pin_set_dt(&config->cs, 0);
232 }
233 if (gpio_is_ready_dt(&config->reset)) {
234 ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_ACTIVE);
235 if (ret < 0) {
236 failed_pin = "reset";
237 goto fail;
238 }
239 gpio_pin_set_dt(&config->reset, 0);
240 }
241 for (int i = 0; i < config->data_bus_width; i++) {
242 if (gpio_is_ready_dt(&config->data[i])) {
243 ret = gpio_pin_configure_dt(&config->data[i], GPIO_OUTPUT_ACTIVE);
244 if (ret < 0) {
245 failed_pin = "data";
246 goto fail;
247 }
248 gpio_pin_set_dt(&config->data[i], 0);
249 }
250 }
251
252 #if MIPI_DBI_8_BIT_MODE
253 /* To optimize performance, we test whether all the data pins are
254 * on the same port. If they are, we can set the whole port in one go
255 * instead of setting each pin individually.
256 * For 8-bit mode only because LUT size grows exponentially.
257 */
258 if (config->data_bus_width == 8) {
259 data->single_port = true;
260 data->data_port = config->data[0].port;
261 for (int i = 1; i < config->data_bus_width; i++) {
262 if (data->data_port != config->data[i].port) {
263 data->single_port = false;
264 }
265 }
266 }
267 if (data->single_port) {
268 LOG_DBG("LUT optimization enabled. data_mask=0x%x", config->data_mask);
269 }
270 #endif
271
272 return ret;
273 fail:
274 LOG_ERR("Failed to configure %s GPIO pin.", failed_pin);
275 return ret;
276 }
277
278 static DEVICE_API(mipi_dbi, mipi_dbi_bitbang_driver_api) = {
279 .reset = mipi_dbi_bitbang_reset,
280 .command_write = mipi_dbi_bitbang_command_write,
281 .write_display = mipi_dbi_bitbang_write_display
282 };
283
284 /* This macro is repeatedly called by LISTIFY() at compile-time to generate the data bus LUT */
285 #define LUT_GEN(i, n) (((i & (1 << 0)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 0)) : 0) | \
286 ((i & (1 << 1)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 1)) : 0) | \
287 ((i & (1 << 2)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 2)) : 0) | \
288 ((i & (1 << 3)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 3)) : 0) | \
289 ((i & (1 << 4)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 4)) : 0) | \
290 ((i & (1 << 5)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 5)) : 0) | \
291 ((i & (1 << 6)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 6)) : 0) | \
292 ((i & (1 << 7)) ? (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 7)) : 0))
293
294 /* If at least one instance has an 8-bit bus, add a data look-up table to the read-only config.
295 * Whether or not it is valid and actually used for a particular instance is decided at runtime
296 * and stored in the instance's mipi_dbi_bitbang_data.single_port.
297 */
298 #if MIPI_DBI_8_BIT_MODE
299 #define DATA_LUT_OPTIMIZATION(n) \
300 .data_lut = { LISTIFY(256, LUT_GEN, (,), n) }, \
301 .data_mask = ((1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 0)) | \
302 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 1)) | \
303 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 2)) | \
304 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 3)) | \
305 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 4)) | \
306 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 5)) | \
307 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 6)) | \
308 (1 << DT_INST_GPIO_PIN_BY_IDX(n, data_gpios, 7)))
309 #else
310 #define DATA_LUT_OPTIMIZATION(n)
311 #endif
312
313 #define MIPI_DBI_BITBANG_INIT(n) \
314 static const struct mipi_dbi_bitbang_config mipi_dbi_bitbang_config_##n = { \
315 .data = {GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 0, {0}), \
316 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 1, {0}), \
317 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 2, {0}), \
318 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 3, {0}), \
319 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 4, {0}), \
320 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 5, {0}), \
321 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 6, {0}), \
322 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 7, {0}), \
323 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 8, {0}), \
324 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 9, {0}), \
325 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 10, {0}), \
326 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 11, {0}), \
327 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 12, {0}), \
328 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 13, {0}), \
329 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 14, {0}), \
330 GPIO_DT_SPEC_INST_GET_BY_IDX_OR(n, data_gpios, 15, {0})}, \
331 .data_bus_width = DT_INST_PROP_LEN(n, data_gpios), \
332 .rd = GPIO_DT_SPEC_INST_GET_OR(n, rd_gpios, {}), \
333 .wr = GPIO_DT_SPEC_INST_GET_OR(n, wr_gpios, {}), \
334 .e = GPIO_DT_SPEC_INST_GET_OR(n, e_gpios, {}), \
335 .cs = GPIO_DT_SPEC_INST_GET_OR(n, cs_gpios, {}), \
336 .cmd_data = GPIO_DT_SPEC_INST_GET_OR(n, dc_gpios, {}), \
337 .reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
338 DATA_LUT_OPTIMIZATION(n) \
339 }; \
340 BUILD_ASSERT(DT_INST_PROP_LEN(n, data_gpios) <= MIPI_DBI_MAX_DATA_BUS_WIDTH, \
341 "Number of data GPIOs in DT exceeds MIPI_DBI_MAX_DATA_BUS_WIDTH"); \
342 static struct mipi_dbi_bitbang_data mipi_dbi_bitbang_data_##n; \
343 DEVICE_DT_INST_DEFINE(n, mipi_dbi_bitbang_init, NULL, &mipi_dbi_bitbang_data_##n, \
344 &mipi_dbi_bitbang_config_##n, POST_KERNEL, \
345 CONFIG_MIPI_DBI_INIT_PRIORITY, &mipi_dbi_bitbang_driver_api);
346
347 DT_INST_FOREACH_STATUS_OKAY(MIPI_DBI_BITBANG_INIT)
348