1 /*
2 * Copyright (c) 2024 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 <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/clock_control/renesas_ra_cgc.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/mipi_dsi.h>
15 #include <zephyr/drivers/reset.h>
16 #include <zephyr/irq.h>
17 #include <zephyr/logging/log.h>
18 #include "r_mipi_dsi.h"
19 #include "r_mipi_phy.h"
20
21 LOG_MODULE_REGISTER(dsi_renesas_ra, CONFIG_MIPI_DSI_LOG_LEVEL);
22
23 /* MIPI PHY Macros */
24 #define MIPI_PHY_CLKSTPT (1183)
25 #define MIPI_PHY_CLKBFHT (11)
26 #define MIPI_PHY_CLKKPT (26)
27 #define MIPI_PHY_GOLPBKT (40)
28
29 #define MIPI_PHY_TINIT (71999)
30 #define MIPI_PHY_TCLKPREP (8)
31 #define MIPI_PHY_THSPREP (5)
32 #define MIPI_PHY_TCLKTRAIL (7)
33 #define MIPI_PHY_TCLKPOST (19)
34 #define MIPI_PHY_TCLKPRE (1)
35 #define MIPI_PHY_TCLKZERO (27)
36 #define MIPI_PHY_THSEXIT (11)
37 #define MIPI_PHY_THSTRAIL (8)
38 #define MIPI_PHY_THSZERO (19)
39 #define MIPI_PHY_TLPEXIT (7)
40 #define LP_DIVISOR (4)
41 #define PLL_MUL_SETTING (49)
42 #define VIDEO_MODE_DELAY (186)
43 #define ULPS_WAKEUP_PERIOD (97)
44 struct mipi_dsi_renesas_ra_config {
45 const struct device *clock_dev;
46 struct clock_control_ra_subsys_cfg clock_dsi_subsys;
47 void (*irq_configure)(void);
48 };
49
50 struct mipi_dsi_renesas_ra_data {
51 mipi_dsi_instance_ctrl_t mipi_dsi_ctrl;
52 mipi_dsi_cfg_t mipi_dsi_cfg;
53 volatile bool message_sent;
54 volatile bool fatal_error;
55 };
56
57 void mipi_dsi_seq0(void);
58 void mipi_dsi_ferr(void);
59 void mipi_dsi_callback(mipi_dsi_callback_args_t *p_args);
60
61 typedef struct {
62 unsigned char size;
63 unsigned char buffer[256];
64 mipi_dsi_cmd_id_t cmd_id;
65 mipi_dsi_cmd_flag_t flags;
66 } lcd_table_setting_t;
67
mipi_dsi_callback(mipi_dsi_callback_args_t * p_args)68 void mipi_dsi_callback(mipi_dsi_callback_args_t *p_args)
69 {
70 const struct device *dev = (struct device *)p_args->p_context;
71 struct mipi_dsi_renesas_ra_data *data = dev->data;
72
73 switch (p_args->event) {
74 case MIPI_DSI_EVENT_SEQUENCE_0: {
75 if (MIPI_DSI_SEQUENCE_STATUS_DESCRIPTORS_FINISHED == p_args->tx_status) {
76 data->message_sent = true;
77 }
78 break;
79 }
80 case MIPI_DSI_EVENT_FATAL: {
81 data->fatal_error = true;
82 break;
83 }
84 default: {
85 break;
86 }
87 }
88 }
89
mipi_dsi_renesas_ra_attach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)90 static int mipi_dsi_renesas_ra_attach(const struct device *dev, uint8_t channel,
91 const struct mipi_dsi_device *mdev)
92 {
93 struct mipi_dsi_renesas_ra_data *data = dev->data;
94 mipi_dsi_cfg_t cfg = data->mipi_dsi_cfg;
95 int ret;
96
97 if (!(mdev->mode_flags & MIPI_DSI_MODE_VIDEO)) {
98 LOG_ERR("DSI host supports video mode only!");
99 return -ENOTSUP;
100 }
101 cfg.virtual_channel_id = channel;
102 cfg.num_lanes = mdev->data_lanes;
103 if (mdev->pixfmt == MIPI_DSI_PIXFMT_RGB888) {
104 cfg.data_type = MIPI_DSI_VIDEO_DATA_24RGB_PIXEL_STREAM;
105 } else if (mdev->pixfmt == MIPI_DSI_PIXFMT_RGB565) {
106 cfg.data_type = MIPI_DSI_VIDEO_DATA_16RGB_PIXEL_STREAM;
107 }
108 cfg.horizontal_active_lines = mdev->timings.hactive;
109 cfg.horizontal_front_porch = mdev->timings.hfp;
110 cfg.horizontal_back_porch = mdev->timings.hbp;
111 cfg.horizontal_sync_lines = mdev->timings.hsync;
112
113 cfg.vertical_active_lines = mdev->timings.vactive;
114 cfg.vertical_front_porch = mdev->timings.vfp;
115 cfg.vertical_back_porch = mdev->timings.vbp;
116 cfg.vertical_sync_lines = mdev->timings.vsync;
117
118 ret = R_MIPI_DSI_Open(&data->mipi_dsi_ctrl, &cfg);
119 if (ret) {
120 LOG_ERR("Open DSI failed (%d)", ret);
121 return -EIO;
122 }
123
124 ret = R_MIPI_DSI_Start(&data->mipi_dsi_ctrl);
125 if (ret) {
126 LOG_ERR("Start DSI host failed! (%d)", ret);
127 return -EIO;
128 }
129
130 return 0;
131 }
132
mipi_dsi_renesas_ra_transfer(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)133 static ssize_t mipi_dsi_renesas_ra_transfer(const struct device *dev, uint8_t channel,
134 struct mipi_dsi_msg *msg)
135 {
136 struct mipi_dsi_renesas_ra_data *data = dev->data;
137 ssize_t len;
138 int ret;
139 uint8_t combined_tx_buffer[msg->tx_len + 1];
140
141 combined_tx_buffer[0] = msg->cmd;
142 memcpy(&combined_tx_buffer[1], msg->tx_buf, msg->tx_len);
143
144 mipi_dsi_cmd_t fsp_msg = {
145 .channel = channel,
146 .cmd_id = msg->type,
147 .flags = MIPI_DSI_CMD_FLAG_LOW_POWER,
148 .tx_len = msg->tx_len + 1,
149 .p_tx_buffer = combined_tx_buffer,
150 };
151 data->message_sent = false;
152 data->fatal_error = false;
153
154 switch (msg->type) {
155 case MIPI_DSI_DCS_READ:
156 LOG_ERR("DCS Read not yet implemented or used");
157 return -ENOTSUP;
158 case MIPI_DSI_DCS_SHORT_WRITE:
159 case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
160 case MIPI_DSI_DCS_LONG_WRITE:
161 ret = R_MIPI_DSI_Command(&data->mipi_dsi_ctrl, &fsp_msg);
162 if (ret) {
163 LOG_ERR("DSI write fail: err: (%d)", ret);
164 return -EIO;
165 }
166 while (!(data->message_sent)) {
167 if (data->fatal_error) {
168 LOG_ERR("fatal error");
169 return -EIO;
170 }
171 }
172 len = msg->tx_len;
173 break;
174 default:
175 LOG_ERR("Unsupported message type (%d)", msg->type);
176 return -ENOTSUP;
177 }
178
179 return len;
180 }
181
182 static DEVICE_API(mipi_dsi, mipi_dsi_api) = {
183 .attach = mipi_dsi_renesas_ra_attach,
184 .transfer = mipi_dsi_renesas_ra_transfer,
185 };
186
mipi_dsi_renesas_ra_init(const struct device * dev)187 static int mipi_dsi_renesas_ra_init(const struct device *dev)
188 {
189 const struct mipi_dsi_renesas_ra_config *config = dev->config;
190 struct mipi_dsi_renesas_ra_data *data = dev->data;
191 int ret;
192
193 if (!device_is_ready(config->clock_dev)) {
194 LOG_ERR("clock control device not ready");
195 return -ENODEV;
196 }
197
198 ret = clock_control_on(config->clock_dev,
199 (clock_control_subsys_t)&config->clock_dsi_subsys);
200 if (ret) {
201 LOG_ERR("Enable DSI peripheral clock failed! (%d)", ret);
202 return ret;
203 }
204
205 config->irq_configure();
206 data->mipi_dsi_cfg.p_context = dev;
207
208 return 0;
209 }
210
211 #define IRQ_CONFIGURE_FUNC(id) \
212 static void mipi_dsi_ra_configure_func_##id(void) \
213 { \
214 R_ICU->IELSR[DT_INST_IRQ_BY_NAME(id, sq0, irq)] = ELC_EVENT_MIPIDSI_SEQ0; \
215 IRQ_CONNECT(DT_INST_IRQ_BY_NAME(id, sq0, irq), \
216 DT_INST_IRQ_BY_NAME(id, sq0, priority), mipi_dsi_seq0, \
217 DEVICE_DT_INST_GET(id), 0); \
218 irq_enable(DT_INST_IRQ_BY_NAME(id, sq0, irq)); \
219 R_ICU->IELSR[DT_INST_IRQ_BY_NAME(id, ferr, irq)] = ELC_EVENT_MIPIDSI_FERR; \
220 IRQ_CONNECT(DT_INST_IRQ_BY_NAME(id, ferr, irq), \
221 DT_INST_IRQ_BY_NAME(id, ferr, priority), mipi_dsi_ferr, \
222 DEVICE_DT_INST_GET(id), 0); \
223 irq_enable(DT_INST_IRQ_BY_NAME(id, ferr, irq)); \
224 }
225
226 #define IRQ_CONFIGURE_DEFINE(id) .irq_configure = mipi_dsi_ra_configure_func_##id
227
228 #define RENESAS_MIPI_DSI_DEVICE(id) \
229 IRQ_CONFIGURE_FUNC(id) \
230 mipi_phy_ctrl_t mipi_phy_##id##_ctrl; \
231 static const mipi_phy_timing_t mipi_phy_##id##_timing = { \
232 .t_init = 0x3FFFF & (uint32_t)MIPI_PHY_TINIT, \
233 .t_clk_prep = (uint8_t)MIPI_PHY_TCLKPREP, \
234 .t_hs_prep = (uint8_t)MIPI_PHY_THSPREP, \
235 .dphytim4_b.t_clk_trail = (uint32_t)MIPI_PHY_TCLKTRAIL, \
236 .dphytim4_b.t_clk_post = (uint32_t)MIPI_PHY_TCLKPOST, \
237 .dphytim4_b.t_clk_pre = (uint32_t)MIPI_PHY_TCLKPRE, \
238 .dphytim4_b.t_clk_zero = (uint32_t)MIPI_PHY_TCLKZERO, \
239 .dphytim5_b.t_hs_exit = (uint32_t)MIPI_PHY_THSEXIT, \
240 .dphytim5_b.t_hs_trail = (uint32_t)MIPI_PHY_THSTRAIL, \
241 .dphytim5_b.t_hs_zero = (uint32_t)MIPI_PHY_THSZERO, \
242 .t_lp_exit = (uint32_t)MIPI_PHY_TLPEXIT, \
243 }; \
244 static const mipi_phy_cfg_t mipi_phy_##id##_cfg = { \
245 .pll_settings = {.div = 0, .mul_int = PLL_MUL_SETTING, .mul_frac = 0}, \
246 .lp_divisor = LP_DIVISOR, \
247 .p_timing = &mipi_phy_##id##_timing, \
248 }; \
249 static const mipi_phy_instance_t mipi_phy##id = { \
250 .p_ctrl = &mipi_phy_##id##_ctrl, \
251 .p_cfg = &mipi_phy_##id##_cfg, \
252 .p_api = &g_mipi_phy, \
253 }; \
254 static const mipi_dsi_extended_cfg_t mipi_dsi_##id##_extended_cfg = { \
255 .dsi_seq0.ipl = DT_INST_IRQ_BY_NAME(id, sq0, priority), \
256 .dsi_seq0.irq = DT_INST_IRQ_BY_NAME(id, sq0, irq), \
257 .dsi_seq1.ipl = DT_INST_IRQ_BY_NAME(id, sq1, priority), \
258 .dsi_seq1.irq = DT_INST_IRQ_BY_NAME(id, sq1, irq), \
259 .dsi_vin1.ipl = DT_INST_IRQ_BY_NAME(id, vm, priority), \
260 .dsi_vin1.irq = DT_INST_IRQ_BY_NAME(id, vm, irq), \
261 .dsi_rcv.ipl = DT_INST_IRQ_BY_NAME(id, rcv, priority), \
262 .dsi_rcv.irq = DT_INST_IRQ_BY_NAME(id, rcv, irq), \
263 .dsi_ferr.ipl = DT_INST_IRQ_BY_NAME(id, ferr, priority), \
264 .dsi_ferr.irq = DT_INST_IRQ_BY_NAME(id, ferr, irq), \
265 .dsi_ppi.ipl = DT_INST_IRQ_BY_NAME(id, ppi, priority), \
266 .dsi_ppi.irq = DT_INST_IRQ_BY_NAME(id, ppi, irq), \
267 .dsi_rxie = R_DSILINK_RXIER_BTAREND_Msk | R_DSILINK_RXIER_LRXHTO_Msk | \
268 R_DSILINK_RXIER_TATO_Msk | R_DSILINK_RXIER_RXRESP_Msk | \
269 R_DSILINK_RXIER_RXEOTP_Msk | R_DSILINK_RXIER_RXTE_Msk | \
270 R_DSILINK_RXIER_RXACK_Msk | R_DSILINK_RXIER_EXTEDET_Msk | \
271 R_DSILINK_RXIER_MLFERR_Msk | R_DSILINK_RXIER_ECCERRM_Msk | \
272 R_DSILINK_RXIER_UNEXERR_Msk | R_DSILINK_RXIER_WCERR_Msk | \
273 R_DSILINK_RXIER_CRCERR_Msk | R_DSILINK_RXIER_IBERR_Msk | \
274 R_DSILINK_RXIER_RXOVFERR_Msk | R_DSILINK_RXIER_PRTOERR_Msk | \
275 R_DSILINK_RXIER_NORESERR_Msk | R_DSILINK_RXIER_RSIZEERR_Msk | \
276 R_DSILINK_RXIER_ECCERRS_Msk | R_DSILINK_RXIER_RXAKE_Msk | 0x0, \
277 .dsi_ferrie = R_DSILINK_FERRIER_HTXTO_Msk | R_DSILINK_FERRIER_LRXHTO_Msk | \
278 R_DSILINK_FERRIER_TATO_Msk | R_DSILINK_FERRIER_ESCENT_Msk | \
279 R_DSILINK_FERRIER_SYNCESC_Msk | R_DSILINK_FERRIER_CTRL_Msk | \
280 R_DSILINK_FERRIER_CLP0_Msk | R_DSILINK_FERRIER_CLP1_Msk | 0x0, \
281 .dsi_plie = R_DSILINK_PLIER_DLULPENT_Msk | R_DSILINK_PLIER_DLULPEXT_Msk | 0x0, \
282 .dsi_vmie = R_DSILINK_VMIER_VBUFUDF_Msk | R_DSILINK_VMIER_VBUFOVF_Msk | 0x0, \
283 .dsi_sqch0ie = R_DSILINK_SQCH0IER_AACTFIN_Msk | R_DSILINK_SQCH0IER_ADESFIN_Msk | \
284 R_DSILINK_SQCH0IER_TXIBERR_Msk | R_DSILINK_SQCH0IER_RXFERR_Msk | \
285 R_DSILINK_SQCH0IER_RXFAIL_Msk | R_DSILINK_SQCH0IER_RXPFAIL_Msk | \
286 R_DSILINK_SQCH0IER_RXCORERR_Msk | R_DSILINK_SQCH0IER_RXAKE_Msk | \
287 0x0, \
288 .dsi_sqch1ie = R_DSILINK_SQCH1IER_AACTFIN_Msk | R_DSILINK_SQCH1IER_ADESFIN_Msk | \
289 R_DSILINK_SQCH1IER_SIZEERR_Msk | R_DSILINK_SQCH1IER_TXIBERR_Msk | \
290 R_DSILINK_SQCH1IER_RXFERR_Msk | R_DSILINK_SQCH1IER_RXFAIL_Msk | \
291 R_DSILINK_SQCH1IER_RXPFAIL_Msk | R_DSILINK_SQCH1IER_RXCORERR_Msk | \
292 R_DSILINK_SQCH1IER_RXAKE_Msk | 0x0, \
293 }; \
294 static const mipi_dsi_timing_t mipi_dsi_##id##_timing = { \
295 .clock_stop_time = MIPI_PHY_CLKSTPT, \
296 .clock_beforehand_time = MIPI_PHY_CLKBFHT, \
297 .clock_keep_time = MIPI_PHY_CLKKPT, \
298 .go_lp_and_back = MIPI_PHY_GOLPBKT, \
299 }; \
300 static const struct mipi_dsi_renesas_ra_config ra_config_##id = { \
301 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \
302 IRQ_CONFIGURE_DEFINE(id), \
303 .clock_dsi_subsys = {.mstp = (uint32_t)DT_INST_CLOCKS_CELL_BY_IDX(id, 0, mstp), \
304 .stop_bit = DT_INST_CLOCKS_CELL_BY_IDX(id, 0, stop_bit)}}; \
305 static struct mipi_dsi_renesas_ra_data ra_data_##id = { \
306 .mipi_dsi_cfg = \
307 { \
308 .p_mipi_phy_instance = &mipi_phy##id, \
309 .p_timing = &mipi_dsi_##id##_timing, \
310 .sync_pulse = (0), \
311 .data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24, \
312 .vertical_sync_polarity = 1, \
313 .horizontal_sync_polarity = 1, \
314 .video_mode_delay = VIDEO_MODE_DELAY, \
315 .hsa_no_lp = ((0x0) & R_DSILINK_VMSET0R_HSANOLP_Msk), \
316 .hbp_no_lp = ((0x0) & R_DSILINK_VMSET0R_HBPNOLP_Msk), \
317 .hfp_no_lp = ((0x0) & R_DSILINK_VMSET0R_HFPNOLP_Msk), \
318 .num_lanes = \
319 DT_PROP_BY_IDX(DT_NODELABEL(ili9806e), data_lanes, 0), \
320 .ulps_wakeup_period = ULPS_WAKEUP_PERIOD, \
321 .continuous_clock = (1), \
322 .hs_tx_timeout = 0, \
323 .lp_rx_timeout = 0, \
324 .turnaround_timeout = 0, \
325 .bta_timeout = 0, \
326 .lprw_timeout = (0 << R_DSILINK_PRESPTOLPSETR_LPRTO_Pos) | 0, \
327 .hsrw_timeout = (0 << R_DSILINK_PRESPTOHSSETR_HSRTO_Pos) | 0, \
328 .max_return_packet_size = 1, \
329 .ecc_enable = (1), \
330 .crc_check_mask = (mipi_dsi_vc_t)(0x0), \
331 .scramble_enable = (0), \
332 .tearing_detect = (0), \
333 .eotp_enable = (1), \
334 .p_extend = &mipi_dsi_##id##_extended_cfg, \
335 .p_callback = mipi_dsi_callback, \
336 .p_context = NULL, \
337 }, \
338 }; \
339 DEVICE_DT_INST_DEFINE(id, &mipi_dsi_renesas_ra_init, NULL, &ra_data_##id, &ra_config_##id, \
340 POST_KERNEL, CONFIG_MIPI_DSI_INIT_PRIORITY, &mipi_dsi_api);
341
342 DT_INST_FOREACH_STATUS_OKAY(RENESAS_MIPI_DSI_DEVICE)
343