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