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