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