1 /*
2  * Copyright (c) 2024-2025 Renesas Electronics Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT renesas_ra_mipi_dsi
8 
9 #include <soc.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/drivers/clock_control/renesas_ra_cgc.h>
13 #include <zephyr/drivers/mipi_dsi.h>
14 #include "r_mipi_dsi.h"
15 
16 LOG_MODULE_REGISTER(dsi_renesas_ra, CONFIG_MIPI_DSI_LOG_LEVEL);
17 
18 struct mipi_dsi_renesas_ra_config {
19 	const struct device *clock_dev;
20 	struct clock_control_ra_subsys_cfg clock_dsi_subsys;
21 	void (*irq_configure)(void);
22 };
23 
24 struct mipi_dsi_renesas_ra_data {
25 	mipi_dsi_instance_ctrl_t mipi_dsi_ctrl;
26 	mipi_dsi_cfg_t mipi_dsi_cfg;
27 	struct k_sem in_transmission;
28 	atomic_t status;
29 };
30 
31 void mipi_dsi_seq0(void);
32 
mipi_dsi_callback(mipi_dsi_callback_args_t * p_args)33 void mipi_dsi_callback(mipi_dsi_callback_args_t *p_args)
34 {
35 	const struct device *dev = (struct device *)p_args->p_context;
36 	struct mipi_dsi_renesas_ra_data *data = dev->data;
37 
38 	if (p_args->event == MIPI_DSI_EVENT_SEQUENCE_0) {
39 		atomic_set(&data->status, p_args->tx_status);
40 		k_sem_give(&data->in_transmission);
41 	}
42 }
43 
mipi_dsi_renesas_ra_attach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)44 static int mipi_dsi_renesas_ra_attach(const struct device *dev, uint8_t channel,
45 				      const struct mipi_dsi_device *mdev)
46 {
47 	struct mipi_dsi_renesas_ra_data *data = dev->data;
48 	mipi_dsi_cfg_t cfg = data->mipi_dsi_cfg;
49 	fsp_err_t err;
50 
51 	if (!(mdev->mode_flags & MIPI_DSI_MODE_VIDEO)) {
52 		LOG_ERR("DSI host supports video mode only!");
53 		return -ENOTSUP;
54 	}
55 
56 	if (channel == 0 && (mdev->mode_flags & MIPI_DSI_MODE_LPM) == 0) {
57 		LOG_ERR("This channel support LP mode transfer only");
58 		return -ENOTSUP;
59 	}
60 
61 	cfg.virtual_channel_id = channel;
62 	cfg.num_lanes = mdev->data_lanes;
63 
64 	if (mdev->pixfmt == MIPI_DSI_PIXFMT_RGB888) {
65 		cfg.data_type = MIPI_DSI_VIDEO_DATA_24RGB_PIXEL_STREAM;
66 	} else if (mdev->pixfmt == MIPI_DSI_PIXFMT_RGB565) {
67 		cfg.data_type = MIPI_DSI_VIDEO_DATA_16RGB_PIXEL_STREAM;
68 	} else {
69 		return -ENOTSUP;
70 	}
71 
72 	cfg.horizontal_active_lines = mdev->timings.hactive;
73 	cfg.horizontal_front_porch = mdev->timings.hfp;
74 	cfg.horizontal_back_porch = mdev->timings.hbp;
75 	cfg.horizontal_sync_lines = mdev->timings.hsync;
76 
77 	cfg.vertical_active_lines = mdev->timings.vactive;
78 	cfg.vertical_front_porch = mdev->timings.vfp;
79 	cfg.vertical_back_porch = mdev->timings.vbp;
80 	cfg.vertical_sync_lines = mdev->timings.vsync;
81 
82 	err = R_MIPI_DSI_Open(&data->mipi_dsi_ctrl, &cfg);
83 	if (err != FSP_SUCCESS) {
84 		LOG_ERR("Open DSI failed (%d)", err);
85 		return -EIO;
86 	}
87 
88 	err = R_MIPI_DSI_Start(&data->mipi_dsi_ctrl);
89 	if (err != FSP_SUCCESS) {
90 		LOG_ERR("Start DSI host failed! (%d)", err);
91 		return -EIO;
92 	}
93 
94 	return 0;
95 }
96 
97 #define MIPI_DSI_SEQUENCE_STATUS_ERROR                                                             \
98 	(MIPI_DSI_SEQUENCE_STATUS_DESCRIPTOR_ABORT | MIPI_DSI_SEQUENCE_STATUS_SIZE_ERROR |         \
99 	 MIPI_DSI_SEQUENCE_STATUS_TX_INTERNAL_BUS_ERROR |                                          \
100 	 MIPI_DSI_SEQUENCE_STATUS_RX_FATAL_ERROR | MIPI_DSI_SEQUENCE_STATUS_RX_FAIL |              \
101 	 MIPI_DSI_SEQUENCE_STATUS_RX_PACKET_DATA_FAIL |                                            \
102 	 MIPI_DSI_SEQUENCE_STATUS_RX_CORRECTABLE_ERROR |                                           \
103 	 MIPI_DSI_SEQUENCE_STATUS_RX_ACK_AND_ERROR)
104 
mipi_dsi_renesas_ra_dcs_write(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)105 static ssize_t mipi_dsi_renesas_ra_dcs_write(const struct device *dev, uint8_t channel,
106 					     struct mipi_dsi_msg *msg)
107 {
108 	struct mipi_dsi_renesas_ra_data *data = dev->data;
109 	uint8_t payload[msg->tx_len + 1];
110 	mipi_dsi_cmd_t fsp_msg = {.channel = channel,
111 				  .cmd_id = msg->type,
112 				  .p_tx_buffer = payload,
113 				  .tx_len = msg->tx_len + 1,
114 				  .flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) != 0
115 						   ? MIPI_DSI_CMD_FLAG_LOW_POWER
116 						   : 0};
117 
118 	payload[0] = msg->cmd;
119 	memcpy(&payload[1], msg->tx_buf, msg->tx_len);
120 
121 	atomic_clear(&data->status);
122 	k_sem_reset(&data->in_transmission);
123 
124 	if (R_MIPI_DSI_Command(&data->mipi_dsi_ctrl, &fsp_msg) != FSP_SUCCESS) {
125 		LOG_ERR("DSI write fail");
126 		return -EIO;
127 	}
128 
129 	k_sem_take(&data->in_transmission, K_FOREVER);
130 
131 	if ((data->status & MIPI_DSI_SEQUENCE_STATUS_ERROR) != MIPI_DSI_SEQUENCE_STATUS_NONE) {
132 		return -EIO;
133 	}
134 
135 	return (ssize_t)msg->tx_len;
136 }
137 
mipi_dsi_renesas_ra_generic_write(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)138 static ssize_t mipi_dsi_renesas_ra_generic_write(const struct device *dev, uint8_t channel,
139 						 struct mipi_dsi_msg *msg)
140 {
141 	struct mipi_dsi_renesas_ra_data *data = dev->data;
142 	mipi_dsi_cmd_t fsp_msg = {.channel = channel,
143 				  .cmd_id = msg->type,
144 				  .p_tx_buffer = msg->tx_buf,
145 				  .tx_len = msg->tx_len,
146 				  .flags = (msg->flags & MIPI_DSI_MSG_USE_LPM) != 0
147 						   ? MIPI_DSI_CMD_FLAG_LOW_POWER
148 						   : 0};
149 
150 	atomic_clear(&data->status);
151 	k_sem_reset(&data->in_transmission);
152 
153 	if (R_MIPI_DSI_Command(&data->mipi_dsi_ctrl, &fsp_msg) != FSP_SUCCESS) {
154 		LOG_ERR("DSI write fail");
155 		return -EIO;
156 	}
157 
158 	k_sem_take(&data->in_transmission, K_FOREVER);
159 
160 	if ((data->status & MIPI_DSI_SEQUENCE_STATUS_ERROR) != MIPI_DSI_SEQUENCE_STATUS_NONE) {
161 		return -EIO;
162 	}
163 
164 	return (ssize_t)msg->tx_len;
165 }
166 
mipi_dsi_renesas_ra_transfer(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)167 static ssize_t mipi_dsi_renesas_ra_transfer(const struct device *dev, uint8_t channel,
168 					    struct mipi_dsi_msg *msg)
169 {
170 	if (channel == 0 && (msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) {
171 		LOG_ERR("This channel support LP mode transfer only");
172 		return -ENOTSUP;
173 	}
174 
175 	switch (msg->type) {
176 	case MIPI_DSI_DCS_SHORT_WRITE:
177 		__fallthrough;
178 	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
179 		__fallthrough;
180 	case MIPI_DSI_DCS_LONG_WRITE:
181 		return mipi_dsi_renesas_ra_dcs_write(dev, channel, msg);
182 	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
183 		__fallthrough;
184 	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
185 		__fallthrough;
186 	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
187 		__fallthrough;
188 	case MIPI_DSI_GENERIC_LONG_WRITE:
189 		return mipi_dsi_renesas_ra_generic_write(dev, channel, msg);
190 	default:
191 		return -ENOTSUP;
192 	}
193 
194 	return 0;
195 }
196 
197 static DEVICE_API(mipi_dsi, mipi_dsi_api) = {
198 	.attach = mipi_dsi_renesas_ra_attach,
199 	.transfer = mipi_dsi_renesas_ra_transfer,
200 };
201 
mipi_dsi_renesas_ra_init(const struct device * dev)202 static int mipi_dsi_renesas_ra_init(const struct device *dev)
203 {
204 	const struct mipi_dsi_renesas_ra_config *config = dev->config;
205 	struct mipi_dsi_renesas_ra_data *data = dev->data;
206 	int ret;
207 
208 	if (!device_is_ready(config->clock_dev)) {
209 		LOG_ERR("clock control device not ready");
210 		return -ENODEV;
211 	}
212 
213 	ret = clock_control_on(config->clock_dev,
214 			       (clock_control_subsys_t)&config->clock_dsi_subsys);
215 	if (ret != 0) {
216 		LOG_ERR("Enable DSI peripheral clock failed! (%d)", ret);
217 		return ret;
218 	}
219 
220 	k_sem_init(&data->in_transmission, 0, 1);
221 
222 	config->irq_configure();
223 
224 	return 0;
225 }
226 
227 #define RENESAS_RA_MIPI_PHYS_SETTING_DEFINE(n)                                                     \
228 	static const mipi_phy_timing_t mipi_phy_##n##_timing = {                                   \
229 		.t_init = CLAMP(DT_PROP(DT_INST_CHILD(n, phys_timing), t_init), 0, 0x7FFF),        \
230 		.t_clk_prep = CLAMP(DT_PROP(DT_INST_CHILD(n, phys_timing), t_clk_prep), 0, 0xFF),  \
231 		.t_hs_prep = CLAMP(DT_PROP(DT_INST_CHILD(n, phys_timing), t_hs_prep), 0, 0xFF),    \
232 		.t_lp_exit = CLAMP(DT_PROP(DT_INST_CHILD(n, phys_timing), t_lp_exit), 0, 0xFF),    \
233 		.dphytim4_b =                                                                      \
234 			{                                                                          \
235 				.t_clk_zero = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),        \
236 							     dphytim4, 0),                         \
237 				.t_clk_pre = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),         \
238 							    dphytim4, 1),                          \
239 				.t_clk_post = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),        \
240 							     dphytim4, 2),                         \
241 				.t_clk_trail = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),       \
242 							      dphytim4, 3),                        \
243 			},                                                                         \
244 		.dphytim5_b =                                                                      \
245 			{                                                                          \
246 				.t_hs_zero = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),         \
247 							    dphytim5, 0),                          \
248 				.t_hs_trail = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),        \
249 							     dphytim5, 1),                         \
250 				.t_hs_exit = DT_PROP_BY_IDX(DT_INST_CHILD(n, phys_timing),         \
251 							    dphytim5, 2),                          \
252 			},                                                                         \
253 	};                                                                                         \
254                                                                                                    \
255 	static const mipi_phy_cfg_t mipi_phy_##n##_cfg = {                                         \
256 		.pll_settings =                                                                    \
257 			{                                                                          \
258 				.div = DT_INST_PROP(n, pll_div) - 1,                               \
259 				.mul_frac = DT_INST_ENUM_IDX(n, pll_mul_frac),                     \
260 				.mul_int = CLAMP(DT_INST_PROP(n, pll_mul_int), 20, 180) - 1,       \
261 			},                                                                         \
262 		.lp_divisor = CLAMP(DT_INST_PROP(n, lp_divisor), 1, 32) - 1,                       \
263 		.p_timing = &mipi_phy_##n##_timing,                                                \
264 	};                                                                                         \
265                                                                                                    \
266 	mipi_phy_ctrl_t mipi_phy_##n##_ctrl;                                                       \
267                                                                                                    \
268 	static const mipi_phy_instance_t mipi_phy##n = {                                           \
269 		.p_ctrl = &mipi_phy_##n##_ctrl,                                                    \
270 		.p_cfg = &mipi_phy_##n##_cfg,                                                      \
271 		.p_api = &g_mipi_phy,                                                              \
272 	};
273 
274 #define RENESAS_RA_MIPI_DSI_PHYS_GET(n) &mipi_phy##n
275 
276 #define RENESAS_RA_MIPI_DSI_TIMING_DEFINE(n)                                                       \
277 	static const mipi_dsi_timing_t mipi_dsi_##n##_timing = {                                   \
278 		.clock_stop_time = DT_INST_PROP_BY_IDX(n, timing, 0),                              \
279 		.clock_beforehand_time = DT_INST_PROP_BY_IDX(n, timing, 1),                        \
280 		.clock_keep_time = DT_INST_PROP_BY_IDX(n, timing, 2),                              \
281 		.go_lp_and_back = DT_INST_PROP_BY_IDX(n, timing, 3),                               \
282 	};
283 
284 #define RENESAS_RA_MIPI_DSI_TIMING_GET(n) &mipi_dsi_##n##_timing
285 
286 #define RENESAS_MIPI_DSI_DEVICE(id)                                                                \
287 	static void mipi_dsi_ra_configure_func_##id(void)                                          \
288 	{                                                                                          \
289 		R_ICU->IELSR[DT_INST_IRQ_BY_NAME(id, sq0, irq)] =                                  \
290 			BSP_PRV_IELS_ENUM(EVENT_MIPIDSI_SEQ0);                                     \
291 		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(id, sq0, irq),                                     \
292 			    DT_INST_IRQ_BY_NAME(id, sq0, priority), mipi_dsi_seq0, NULL, 0);       \
293 		irq_enable(DT_INST_IRQ_BY_NAME(id, sq0, irq));                                     \
294 	}                                                                                          \
295                                                                                                    \
296 	RENESAS_RA_MIPI_DSI_TIMING_DEFINE(id)                                                      \
297 	RENESAS_RA_MIPI_PHYS_SETTING_DEFINE(id)                                                    \
298                                                                                                    \
299 	static const mipi_dsi_extended_cfg_t mipi_dsi_##id##_extended_cfg = {                      \
300 		.dsi_seq0.ipl = DT_INST_IRQ_BY_NAME(id, sq0, priority),                            \
301 		.dsi_seq0.irq = DT_INST_IRQ_BY_NAME(id, sq0, irq),                                 \
302 		.dsi_seq1.ipl = DT_INST_IRQ_BY_NAME(id, sq1, priority),                            \
303 		.dsi_seq1.irq = DT_INST_IRQ_BY_NAME(id, sq1, irq),                                 \
304 		.dsi_vin1.ipl = DT_INST_IRQ_BY_NAME(id, vm, priority),                             \
305 		.dsi_vin1.irq = DT_INST_IRQ_BY_NAME(id, vm, irq),                                  \
306 		.dsi_rcv.ipl = DT_INST_IRQ_BY_NAME(id, rcv, priority),                             \
307 		.dsi_rcv.irq = DT_INST_IRQ_BY_NAME(id, rcv, irq),                                  \
308 		.dsi_ferr.ipl = DT_INST_IRQ_BY_NAME(id, ferr, priority),                           \
309 		.dsi_ferr.irq = DT_INST_IRQ_BY_NAME(id, ferr, irq),                                \
310 		.dsi_ppi.ipl = DT_INST_IRQ_BY_NAME(id, ppi, priority),                             \
311 		.dsi_ppi.irq = DT_INST_IRQ_BY_NAME(id, ppi, irq),                                  \
312 		.dsi_rxie = R_DSILINK_RXIER_BTAREND_Msk | R_DSILINK_RXIER_LRXHTO_Msk |             \
313 			    R_DSILINK_RXIER_TATO_Msk | R_DSILINK_RXIER_RXRESP_Msk |                \
314 			    R_DSILINK_RXIER_RXEOTP_Msk | R_DSILINK_RXIER_RXTE_Msk |                \
315 			    R_DSILINK_RXIER_RXACK_Msk | R_DSILINK_RXIER_EXTEDET_Msk |              \
316 			    R_DSILINK_RXIER_MLFERR_Msk | R_DSILINK_RXIER_ECCERRM_Msk |             \
317 			    R_DSILINK_RXIER_UNEXERR_Msk | R_DSILINK_RXIER_WCERR_Msk |              \
318 			    R_DSILINK_RXIER_CRCERR_Msk | R_DSILINK_RXIER_IBERR_Msk |               \
319 			    R_DSILINK_RXIER_RXOVFERR_Msk | R_DSILINK_RXIER_PRTOERR_Msk |           \
320 			    R_DSILINK_RXIER_NORESERR_Msk | R_DSILINK_RXIER_RSIZEERR_Msk |          \
321 			    R_DSILINK_RXIER_ECCERRS_Msk | R_DSILINK_RXIER_RXAKE_Msk,               \
322 		.dsi_ferrie = R_DSILINK_FERRIER_HTXTO_Msk | R_DSILINK_FERRIER_LRXHTO_Msk |         \
323 			      R_DSILINK_FERRIER_TATO_Msk | R_DSILINK_FERRIER_ESCENT_Msk |          \
324 			      R_DSILINK_FERRIER_SYNCESC_Msk | R_DSILINK_FERRIER_CTRL_Msk |         \
325 			      R_DSILINK_FERRIER_CLP0_Msk | R_DSILINK_FERRIER_CLP1_Msk,             \
326 		.dsi_plie = R_DSILINK_PLIER_DLULPENT_Msk | R_DSILINK_PLIER_DLULPEXT_Msk,           \
327 		.dsi_vmie = R_DSILINK_VMIER_VBUFUDF_Msk | R_DSILINK_VMIER_VBUFOVF_Msk,             \
328 		.dsi_sqch0ie = R_DSILINK_SQCH0IER_AACTFIN_Msk | R_DSILINK_SQCH0IER_ADESFIN_Msk |   \
329 			       R_DSILINK_SQCH0IER_TXIBERR_Msk | R_DSILINK_SQCH0IER_RXFERR_Msk |    \
330 			       R_DSILINK_SQCH0IER_RXFAIL_Msk | R_DSILINK_SQCH0IER_RXPFAIL_Msk |    \
331 			       R_DSILINK_SQCH0IER_RXCORERR_Msk | R_DSILINK_SQCH0IER_RXAKE_Msk,     \
332 		.dsi_sqch1ie = R_DSILINK_SQCH1IER_AACTFIN_Msk | R_DSILINK_SQCH1IER_ADESFIN_Msk |   \
333 			       R_DSILINK_SQCH1IER_SIZEERR_Msk | R_DSILINK_SQCH1IER_TXIBERR_Msk |   \
334 			       R_DSILINK_SQCH1IER_RXFERR_Msk | R_DSILINK_SQCH1IER_RXFAIL_Msk |     \
335 			       R_DSILINK_SQCH1IER_RXPFAIL_Msk | R_DSILINK_SQCH1IER_RXCORERR_Msk |  \
336 			       R_DSILINK_SQCH1IER_RXAKE_Msk,                                       \
337 	};                                                                                         \
338                                                                                                    \
339 	static const struct mipi_dsi_renesas_ra_config ra_config_##id = {                          \
340 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)),                               \
341 		.clock_dsi_subsys =                                                                \
342 			{                                                                          \
343 				.mstp = DT_INST_CLOCKS_CELL(id, mstp),                             \
344 				.stop_bit = DT_INST_CLOCKS_CELL(id, stop_bit),                     \
345 			},                                                                         \
346 		.irq_configure = mipi_dsi_ra_configure_func_##id,                                  \
347 	};                                                                                         \
348                                                                                                    \
349 	static struct mipi_dsi_renesas_ra_data ra_data_##id = {                                    \
350 		.mipi_dsi_cfg =                                                                    \
351 			{                                                                          \
352 				.p_mipi_phy_instance = RENESAS_RA_MIPI_DSI_PHYS_GET(id),           \
353 				.p_timing = RENESAS_RA_MIPI_DSI_TIMING_GET(id),                    \
354 				.sync_pulse = (0),                                                 \
355 				.vertical_sync_polarity = 1,                                       \
356 				.horizontal_sync_polarity = 1,                                     \
357 				.video_mode_delay = DT_INST_PROP(id, video_mode_delay),            \
358 				.hsa_no_lp = R_DSILINK_VMSET0R_HSANOLP_Msk,                        \
359 				.hbp_no_lp = R_DSILINK_VMSET0R_HBPNOLP_Msk,                        \
360 				.hfp_no_lp = R_DSILINK_VMSET0R_HFPNOLP_Msk,                        \
361 				.ulps_wakeup_period = DT_INST_PROP(id, ulps_wakeup_period),        \
362 				.continuous_clock = (1),                                           \
363 				.hs_tx_timeout = 0,                                                \
364 				.lp_rx_timeout = 0,                                                \
365 				.turnaround_timeout = 0,                                           \
366 				.bta_timeout = 0,                                                  \
367 				.lprw_timeout = 0,                                                 \
368 				.hsrw_timeout = 0,                                                 \
369 				.max_return_packet_size = 1,                                       \
370 				.ecc_enable = (1),                                                 \
371 				.crc_check_mask = (mipi_dsi_vc_t)(0x0),                            \
372 				.scramble_enable = (0),                                            \
373 				.tearing_detect = (0),                                             \
374 				.eotp_enable = (1),                                                \
375 				.p_extend = &mipi_dsi_##id##_extended_cfg,                         \
376 				.p_callback = mipi_dsi_callback,                                   \
377 				.p_context = DEVICE_DT_INST_GET(id),                               \
378 			},                                                                         \
379 	};                                                                                         \
380                                                                                                    \
381 	DEVICE_DT_INST_DEFINE(id, &mipi_dsi_renesas_ra_init, NULL, &ra_data_##id, &ra_config_##id, \
382 			      POST_KERNEL, CONFIG_MIPI_DSI_INIT_PRIORITY, &mipi_dsi_api);
383 
384 DT_INST_FOREACH_STATUS_OKAY(RENESAS_MIPI_DSI_DEVICE)
385