1 /*
2  * Copyright 2023 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Public APIs for MIPI-DBI drivers
10  *
11  * MIPI-DBI defines the following 3 interfaces:
12  * Type A: Motorola 6800 type parallel bus
13  * Type B: Intel 8080 type parallel bus
14  * Type C: SPI Type (1 bit bus) with 3 options:
15  *     1. 9 write clocks per byte, final bit is command/data selection bit
16  *     2. Same as above, but 16 write clocks per byte
17  *     3. 8 write clocks per byte. Command/data selected via GPIO pin
18  * The current driver interface does not support type C with 16 write clocks (option 2).
19  */
20 
21 #ifndef ZEPHYR_INCLUDE_DRIVERS_MIPI_DBI_H_
22 #define ZEPHYR_INCLUDE_DRIVERS_MIPI_DBI_H_
23 
24 /**
25  * @brief MIPI-DBI driver APIs
26  * @defgroup mipi_dbi_interface MIPI-DBI driver APIs
27  * @since 3.6
28  * @version 0.1.0
29  * @ingroup io_interfaces
30  * @{
31  */
32 
33 #include <zephyr/device.h>
34 #include <zephyr/drivers/display.h>
35 #include <zephyr/display/mipi_display.h>
36 #include <zephyr/drivers/spi.h>
37 #include <zephyr/dt-bindings/mipi_dbi/mipi_dbi.h>
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 /**
44  * @brief initialize a MIPI DBI SPI configuration struct from devicetree
45  *
46  * This helper allows drivers to initialize a MIPI DBI SPI configuration
47  * structure using devicetree.
48  * @param node_id Devicetree node identifier for the MIPI DBI device whose
49  *                struct spi_config to create an initializer for
50  * @param operation_ the desired operation field in the struct spi_config
51  * @param delay_ the desired delay field in the struct spi_config's
52  *               spi_cs_control, if there is one
53  */
54 #define MIPI_DBI_SPI_CONFIG_DT(node_id, operation_, delay_)		\
55 	{								\
56 		.frequency = DT_PROP(node_id, mipi_max_frequency),	\
57 		.operation = (operation_) |				\
58 			DT_PROP_OR(node_id, duplex, 0) |			\
59 			COND_CODE_1(DT_PROP(node_id, mipi_cpol), SPI_MODE_CPOL, (0)) |	\
60 			COND_CODE_1(DT_PROP(node_id, mipi_cpha), SPI_MODE_CPHA, (0)) |	\
61 			COND_CODE_1(DT_PROP(node_id, mipi_hold_cs), SPI_HOLD_ON_CS, (0)),	\
62 		.slave = DT_REG_ADDR(node_id),				\
63 		.cs = {							\
64 			.gpio = GPIO_DT_SPEC_GET_BY_IDX_OR(DT_PHANDLE(DT_PARENT(node_id), \
65 							   spi_dev), cs_gpios, \
66 							   DT_REG_ADDR_RAW(node_id), \
67 							   {}),		\
68 			.delay = (delay_),				\
69 		},							\
70 	}
71 
72 /**
73  * @brief Initialize a MIPI DBI SPI configuration from devicetree instance
74  *
75  * This helper initializes a MIPI DBI SPI configuration from a devicetree
76  * instance. It is equivalent to MIPI_DBI_SPI_CONFIG_DT(DT_DRV_INST(inst))
77  * @param inst Instance number to initialize configuration from
78  * @param operation_ the desired operation field in the struct spi_config
79  * @param delay_ the desired delay field in the struct spi_config's
80  *               spi_cs_control, if there is one
81  */
82 #define MIPI_DBI_SPI_CONFIG_DT_INST(inst, operation_, delay_)		\
83 	MIPI_DBI_SPI_CONFIG_DT(DT_DRV_INST(inst), operation_, delay_)
84 
85 /**
86  * @brief Initialize a MIPI DBI configuration from devicetree
87  *
88  * This helper allows drivers to initialize a MIPI DBI configuration
89  * structure from devicetree. It sets the MIPI DBI mode, as well
90  * as configuration fields in the SPI configuration structure
91  * @param node_id Devicetree node identifier for the MIPI DBI device to
92  *                initialize
93  * @param operation_ the desired operation field in the struct spi_config
94  * @param delay_ the desired delay field in the struct spi_config's
95  *               spi_cs_control, if there is one
96  */
97 #define MIPI_DBI_CONFIG_DT(node_id, operation_, delay_)			\
98 	{								\
99 		.mode = DT_STRING_UPPER_TOKEN(node_id, mipi_mode),	\
100 		.config = MIPI_DBI_SPI_CONFIG_DT(node_id, operation_, delay_), \
101 	}
102 
103 /**
104  * @brief Initialize a MIPI DBI configuration from device instance
105  *
106  * Equivalent to MIPI_DBI_CONFIG_DT(DT_DRV_INST(inst), operation_, delay_)
107  * @param inst Instance of the device to initialize a MIPI DBI configuration for
108  * @param operation_ the desired operation field in the struct spi_config
109  * @param delay_ the desired delay field in the struct spi_config's
110  *               spi_cs_control, if there is one
111  */
112 #define MIPI_DBI_CONFIG_DT_INST(inst, operation_, delay_)		\
113 	MIPI_DBI_CONFIG_DT(DT_DRV_INST(inst), operation_, delay_)
114 
115 /**
116  * @brief Get the MIPI DBI TE mode from devicetree
117  *
118  * Gets the MIPI DBI TE mode from a devicetree property.
119  * @param node_id Devicetree node identifier for the MIPI DBI device with the
120  *                TE mode property
121  * @param edge_prop Property name for the TE mode that should be read from
122  *                  devicetree
123  */
124 #define MIPI_DBI_TE_MODE_DT(node_id, edge_prop)                           \
125 	DT_STRING_UPPER_TOKEN(node_id, edge_prop)
126 
127 /**
128  * @brief Get the MIPI DBI TE mode for device instance
129  *
130  * Gets the MIPI DBI TE mode from a devicetree property. Equivalent to
131  * MIPI_DBI_TE_MODE_DT(DT_DRV_INST(inst), edge_mode).
132  * @param inst Instance of the device to get the TE mode for
133  * @param edge_prop Property name for the TE mode that should be read from
134  *                  devicetree
135  */
136 #define MIPI_DBI_TE_MODE_DT_INST(inst, edge_prop)                         \
137 	DT_STRING_UPPER_TOKEN(DT_DRV_INST(inst), edge_prop)
138 
139 /**
140  * @brief MIPI DBI controller configuration
141  *
142  * Configuration for MIPI DBI controller write
143  */
144 struct mipi_dbi_config {
145 	/** MIPI DBI mode */
146 	uint8_t mode;
147 	/** SPI configuration */
148 	struct spi_config config;
149 };
150 
151 
152 /** MIPI-DBI host driver API */
153 __subsystem struct mipi_dbi_driver_api {
154 	int (*command_write)(const struct device *dev,
155 			     const struct mipi_dbi_config *config, uint8_t cmd,
156 			     const uint8_t *data, size_t len);
157 	int (*command_read)(const struct device *dev,
158 			    const struct mipi_dbi_config *config, uint8_t *cmds,
159 			    size_t num_cmds, uint8_t *response, size_t len);
160 	int (*write_display)(const struct device *dev,
161 			     const struct mipi_dbi_config *config,
162 			     const uint8_t *framebuf,
163 			     struct display_buffer_descriptor *desc,
164 			     enum display_pixel_format pixfmt);
165 	int (*reset)(const struct device *dev, k_timeout_t delay);
166 	int (*release)(const struct device *dev,
167 		       const struct mipi_dbi_config *config);
168 	int (*configure_te)(const struct device *dev,
169 			    uint8_t edge,
170 			    k_timeout_t delay);
171 };
172 
173 /**
174  * @brief Write a command to the display controller
175  *
176  * Writes a command, along with an optional data buffer to the display.
177  * If data buffer and buffer length are NULL and 0 respectively, then
178  * only a command will be sent. Note that if the SPI configuration passed
179  * to this function locks the SPI bus, it is the caller's responsibility
180  * to release it with mipi_dbi_release()
181  *
182  * @param dev mipi dbi controller
183  * @param config MIPI DBI configuration
184  * @param cmd command to write to display controller
185  * @param data optional data buffer to write after command
186  * @param len size of data buffer in bytes. Set to 0 to skip sending data.
187  * @retval 0 command write succeeded
188  * @retval -EIO I/O error
189  * @retval -ETIMEDOUT transfer timed out
190  * @retval -EBUSY controller is busy
191  * @retval -ENOSYS not implemented
192  */
mipi_dbi_command_write(const struct device * dev,const struct mipi_dbi_config * config,uint8_t cmd,const uint8_t * data,size_t len)193 static inline int mipi_dbi_command_write(const struct device *dev,
194 					 const struct mipi_dbi_config *config,
195 					 uint8_t cmd, const uint8_t *data,
196 					 size_t len)
197 {
198 	const struct mipi_dbi_driver_api *api =
199 		(const struct mipi_dbi_driver_api *)dev->api;
200 
201 	if (api->command_write == NULL) {
202 		return -ENOSYS;
203 	}
204 	return api->command_write(dev, config, cmd, data, len);
205 }
206 
207 /**
208  * @brief Read a command response from the display controller
209  *
210  * Reads a command response from the display controller.
211  *
212  * @param dev mipi dbi controller
213  * @param config MIPI DBI configuration
214  * @param cmds array of one byte commands to send to display controller
215  * @param num_cmd number of commands to write to display controller
216  * @param response response buffer, filled with display controller response
217  * @param len size of response buffer in bytes.
218  * @retval 0 command read succeeded
219  * @retval -EIO I/O error
220  * @retval -ETIMEDOUT transfer timed out
221  * @retval -EBUSY controller is busy
222  * @retval -ENOSYS not implemented
223  */
mipi_dbi_command_read(const struct device * dev,const struct mipi_dbi_config * config,uint8_t * cmds,size_t num_cmd,uint8_t * response,size_t len)224 static inline int mipi_dbi_command_read(const struct device *dev,
225 					const struct mipi_dbi_config *config,
226 					uint8_t *cmds, size_t num_cmd,
227 					uint8_t *response, size_t len)
228 {
229 	const struct mipi_dbi_driver_api *api =
230 		(const struct mipi_dbi_driver_api *)dev->api;
231 
232 	if (api->command_read == NULL) {
233 		return -ENOSYS;
234 	}
235 	return api->command_read(dev, config, cmds, num_cmd, response, len);
236 }
237 
238 /**
239  * @brief Write a display buffer to the display controller.
240  *
241  * Writes a display buffer to the controller. If the controller requires
242  * a "Write memory" command before writing display data, this should be
243  * sent with @ref mipi_dbi_command_write
244  * @param dev mipi dbi controller
245  * @param config MIPI DBI configuration
246  * @param framebuf: framebuffer to write to display
247  * @param desc: descriptor of framebuffer to write. Note that the pitch must
248  *   be equal to width. "buf_size" field determines how many bytes will be
249  *   written.
250  * @param pixfmt: pixel format of framebuffer data
251  * @retval 0 buffer write succeeded.
252  * @retval -EIO I/O error
253  * @retval -ETIMEDOUT transfer timed out
254  * @retval -EBUSY controller is busy
255  * @retval -ENOSYS not implemented
256  */
mipi_dbi_write_display(const struct device * dev,const struct mipi_dbi_config * config,const uint8_t * framebuf,struct display_buffer_descriptor * desc,enum display_pixel_format pixfmt)257 static inline int mipi_dbi_write_display(const struct device *dev,
258 					 const struct mipi_dbi_config *config,
259 					 const uint8_t *framebuf,
260 					 struct display_buffer_descriptor *desc,
261 					 enum display_pixel_format pixfmt)
262 {
263 	const struct mipi_dbi_driver_api *api =
264 		(const struct mipi_dbi_driver_api *)dev->api;
265 
266 	if (api->write_display == NULL) {
267 		return -ENOSYS;
268 	}
269 	return api->write_display(dev, config, framebuf, desc, pixfmt);
270 }
271 
272 /**
273  * @brief Resets attached display controller
274  *
275  * Resets the attached display controller.
276  * @param dev mipi dbi controller
277  * @param delay_ms duration to set reset signal for, in milliseconds
278  * @retval 0 reset succeeded
279  * @retval -EIO I/O error
280  * @retval -ENOSYS not implemented
281  * @retval -ENOTSUP not supported
282  */
mipi_dbi_reset(const struct device * dev,uint32_t delay_ms)283 static inline int mipi_dbi_reset(const struct device *dev, uint32_t delay_ms)
284 {
285 	const struct mipi_dbi_driver_api *api =
286 		(const struct mipi_dbi_driver_api *)dev->api;
287 
288 	if (api->reset == NULL) {
289 		return -ENOSYS;
290 	}
291 	return api->reset(dev, K_MSEC(delay_ms));
292 }
293 
294 /**
295  * @brief Releases a locked MIPI DBI device.
296  *
297  * Releases a lock on a MIPI DBI device and/or the device's CS line if and
298  * only if the given config parameter was the last one to be used in any
299  * of the above functions, and if it has the SPI_LOCK_ON bit set and/or
300  * the SPI_HOLD_ON_CS bit set into its operation bits field.
301  * This lock functions exactly like the SPI lock, and can be used if the caller
302  * needs to keep CS asserted for multiple transactions, or the MIPI DBI device
303  * locked.
304  * @param dev mipi dbi controller
305  * @param config MIPI DBI configuration
306  * @retval 0 reset succeeded
307  * @retval -EIO I/O error
308  * @retval -ENOSYS not implemented
309  * @retval -ENOTSUP not supported
310  */
mipi_dbi_release(const struct device * dev,const struct mipi_dbi_config * config)311 static inline int mipi_dbi_release(const struct device *dev,
312 				   const struct mipi_dbi_config *config)
313 {
314 	const struct mipi_dbi_driver_api *api =
315 		(const struct mipi_dbi_driver_api *)dev->api;
316 
317 	if (api->release == NULL) {
318 		return -ENOSYS;
319 	}
320 	return api->release(dev, config);
321 }
322 
323 /**
324  * @brief Configures MIPI DBI tearing effect signal
325  *
326  * Many displays provide a tearing effect signal, which can be configured
327  * to pulse at each vsync interval or each hsync interval. This signal can be
328  * used by the MCU to determine when to transmit a new frame so that the
329  * read pointer of the display never overlaps with the write pointer from the
330  * MCU. This function configures the MIPI DBI controller to delay transmitting
331  * display frames until the selected tearing effect signal edge occurs.
332  *
333  * The delay will occur on the on each call to @ref mipi_dbi_write_display
334  * where the ``frame_incomplete`` flag was set within the buffer descriptor
335  * provided with the prior call, as this indicates the buffer being written
336  * in this call is the first buffer of a new frame.
337  *
338  * Note that most display controllers will need to enable the TE signal
339  * using vendor specific commands before the MIPI DBI controller can react
340  * to it.
341  *
342  * @param dev mipi dbi controller
343  * @param edge which edge of the TE signal to start transmitting on
344  * @param delay_us how many microseconds after TE edge to start transmission
345  * @retval -EIO I/O error
346  * @retval -ENOSYS not implemented
347  * @retval -ENOTSUP not supported
348  */
mipi_dbi_configure_te(const struct device * dev,uint8_t edge,uint32_t delay_us)349 static inline int mipi_dbi_configure_te(const struct device *dev,
350 					uint8_t edge,
351 					uint32_t delay_us)
352 {
353 	const struct mipi_dbi_driver_api *api =
354 		(const struct mipi_dbi_driver_api *)dev->api;
355 
356 	if (api->configure_te == NULL) {
357 		return -ENOSYS;
358 	}
359 	return api->configure_te(dev, edge, K_USEC(delay_us));
360 }
361 
362 #ifdef __cplusplus
363 }
364 #endif
365 
366 /**
367  * @}
368  */
369 
370 #endif /* ZEPHYR_INCLUDE_DRIVERS_MIPI_DBI_H_ */
371