1 /*
2  * Copyright 2023, NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7  /* Based on dsi_mcux.c, which is (c) 2022 NXP */
8 
9 #define DT_DRV_COMPAT nxp_mipi_dsi_2l
10 
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/mipi_dsi.h>
13 #include <fsl_mipi_dsi.h>
14 #include <fsl_clock.h>
15 #include <zephyr/logging/log.h>
16 
17 #include <soc.h>
18 
19 LOG_MODULE_REGISTER(dsi_mcux_host, CONFIG_MIPI_DSI_LOG_LEVEL);
20 
21 struct mcux_mipi_dsi_config {
22 	MIPI_DSI_HOST_Type *base;
23 	dsi_dpi_config_t dpi_config;
24 	bool auto_insert_eotp;
25 	const struct device *bit_clk_dev;
26 	clock_control_subsys_t bit_clk_subsys;
27 	const struct device *esc_clk_dev;
28 	clock_control_subsys_t esc_clk_subsys;
29 	const struct device *pixel_clk_dev;
30 	clock_control_subsys_t pixel_clk_subsys;
31 	uint32_t dphy_ref_freq;
32 };
33 
dsi_mcux_attach(const struct device * dev,uint8_t channel,const struct mipi_dsi_device * mdev)34 static int dsi_mcux_attach(const struct device *dev,
35 			   uint8_t channel,
36 			   const struct mipi_dsi_device *mdev)
37 {
38 	const struct mcux_mipi_dsi_config *config = dev->config;
39 	dsi_dphy_config_t dphy_config;
40 	dsi_config_t dsi_config;
41 	uint32_t dphy_bit_clk_freq;
42 	uint32_t dphy_esc_clk_freq;
43 	uint32_t dsi_pixel_clk_freq;
44 	uint32_t bit_width;
45 
46 	DSI_GetDefaultConfig(&dsi_config);
47 	dsi_config.numLanes = mdev->data_lanes;
48 	dsi_config.autoInsertEoTp = config->auto_insert_eotp;
49 
50 	/* Init the DSI module. */
51 	DSI_Init(config->base, &dsi_config);
52 
53 	/* Get the DPHY bit clock frequency */
54 	if (clock_control_get_rate(config->bit_clk_dev,
55 				config->bit_clk_subsys,
56 				&dphy_bit_clk_freq)) {
57 		return -EINVAL;
58 	};
59 	/* Get the DPHY ESC clock frequency */
60 	if (clock_control_get_rate(config->esc_clk_dev,
61 				config->esc_clk_subsys,
62 				&dphy_esc_clk_freq)) {
63 		return -EINVAL;
64 	}
65 	/* Get the Pixel clock frequency */
66 	if (clock_control_get_rate(config->pixel_clk_dev,
67 				config->pixel_clk_subsys,
68 				&dsi_pixel_clk_freq)) {
69 		return -EINVAL;
70 	}
71 
72 	switch (config->dpi_config.pixelPacket) {
73 	case kDSI_PixelPacket16Bit:
74 		bit_width = 16;
75 		break;
76 	case kDSI_PixelPacket18Bit:
77 		__fallthrough;
78 	case kDSI_PixelPacket18BitLoosely:
79 		bit_width = 18;
80 		break;
81 	case kDSI_PixelPacket24Bit:
82 		bit_width = 24;
83 		break;
84 	default:
85 		return -EINVAL; /* Invalid bit width enum value? */
86 	}
87 	/* Init DPHY.
88 	 *
89 	 * The DPHY bit clock must be fast enough to send out the pixels, it should be
90 	 * larger than:
91 	 *
92 	 *         (Pixel clock * bit per output pixel) / number of MIPI data lane
93 	 */
94 	if (((dsi_pixel_clk_freq * bit_width) / mdev->data_lanes) > dphy_bit_clk_freq) {
95 		return -EINVAL;
96 	}
97 
98 	DSI_GetDphyDefaultConfig(&dphy_config, dphy_bit_clk_freq, dphy_esc_clk_freq);
99 
100 	if (config->dphy_ref_freq != 0) {
101 		dphy_bit_clk_freq = DSI_InitDphy(config->base,
102 					&dphy_config, config->dphy_ref_freq);
103 	} else {
104 		/* DPHY PLL is not present, ref clock is unused */
105 		DSI_InitDphy(config->base, &dphy_config, 0);
106 	}
107 
108 	/* Init DPI interface. */
109 	DSI_SetDpiConfig(config->base, &config->dpi_config, mdev->data_lanes,
110 					dsi_pixel_clk_freq, dphy_bit_clk_freq);
111 
112 	imxrt_post_init_display_interface();
113 
114 	return 0;
115 }
116 
dsi_mcux_transfer(const struct device * dev,uint8_t channel,struct mipi_dsi_msg * msg)117 static ssize_t dsi_mcux_transfer(const struct device *dev, uint8_t channel,
118 				 struct mipi_dsi_msg *msg)
119 {
120 	const struct mcux_mipi_dsi_config *config = dev->config;
121 	dsi_transfer_t dsi_xfer = {0};
122 	status_t status;
123 
124 	dsi_xfer.virtualChannel = channel;
125 	dsi_xfer.txDataSize = msg->tx_len;
126 	dsi_xfer.txData = msg->tx_buf;
127 	dsi_xfer.rxDataSize = msg->rx_len;
128 	dsi_xfer.rxData = msg->rx_buf;
129 
130 	switch (msg->type) {
131 	case MIPI_DSI_DCS_READ:
132 		LOG_ERR("DCS Read not yet implemented or used");
133 		return -ENOTSUP;
134 	case MIPI_DSI_DCS_SHORT_WRITE:
135 		dsi_xfer.sendDscCmd = true;
136 		dsi_xfer.dscCmd = msg->cmd;
137 		dsi_xfer.txDataType = kDSI_TxDataDcsShortWrNoParam;
138 		break;
139 	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
140 		__fallthrough;
141 	case MIPI_DSI_DCS_LONG_WRITE:
142 		dsi_xfer.sendDscCmd = true;
143 		dsi_xfer.dscCmd = msg->cmd;
144 		dsi_xfer.txDataType = kDSI_TxDataDcsShortWrOneParam;
145 		break;
146 	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
147 		dsi_xfer.txDataType = kDSI_TxDataGenShortWrNoParam;
148 		break;
149 	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
150 		dsi_xfer.txDataType = kDSI_TxDataGenShortWrOneParam;
151 		break;
152 	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
153 		dsi_xfer.txDataType = kDSI_TxDataGenShortWrTwoParam;
154 		break;
155 	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
156 		__fallthrough;
157 	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
158 		__fallthrough;
159 	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
160 		LOG_ERR("Generic Read not yet implemented or used");
161 		return -ENOTSUP;
162 	default:
163 		LOG_ERR("Unsupported message type (%d)", msg->type);
164 		return -ENOTSUP;
165 	}
166 
167 	status = DSI_TransferBlocking(config->base, &dsi_xfer);
168 
169 	if (status != kStatus_Success) {
170 		LOG_ERR("Transmission failed");
171 		return -EIO;
172 	}
173 
174 	if (msg->rx_len != 0) {
175 		/* Return rx_len on a read */
176 		return msg->rx_len;
177 	}
178 
179 	/* Return tx_len on a write */
180 	return msg->tx_len;
181 
182 }
183 
184 static struct mipi_dsi_driver_api dsi_mcux_api = {
185 	.attach = dsi_mcux_attach,
186 	.transfer = dsi_mcux_transfer,
187 };
188 
mcux_mipi_dsi_init(const struct device * dev)189 static int mcux_mipi_dsi_init(const struct device *dev)
190 {
191 	const struct mcux_mipi_dsi_config *config = dev->config;
192 
193 	imxrt_pre_init_display_interface();
194 
195 	if (!device_is_ready(config->bit_clk_dev) ||
196 			!device_is_ready(config->esc_clk_dev) ||
197 			!device_is_ready(config->pixel_clk_dev)) {
198 		return -ENODEV;
199 	}
200 	return 0;
201 }
202 
203 #define MCUX_MIPI_DSI_DEVICE(id)								\
204 	static const struct mcux_mipi_dsi_config mipi_dsi_config_##id = {	\
205 		.base = (MIPI_DSI_HOST_Type *)DT_INST_REG_ADDR(id),				\
206 		.dpi_config = {									\
207 			.dpiColorCoding = DT_INST_ENUM_IDX(id, dpi_color_coding),		\
208 			.pixelPacket = DT_INST_ENUM_IDX(id, dpi_pixel_packet),			\
209 			.videoMode = DT_INST_ENUM_IDX(id, dpi_video_mode),			\
210 			.bllpMode = DT_INST_ENUM_IDX(id, dpi_bllp_mode),			\
211 			.pixelPayloadSize = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, width),	\
212 			.panelHeight = DT_INST_PROP_BY_PHANDLE(id, nxp_lcdif, height),		\
213 			.polarityFlags = (DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),	\
214 						display_timings),  vsync_active) ?		\
215 						kDSI_DpiVsyncActiveHigh :			\
216 						kDSI_DpiVsyncActiveLow) |			\
217 					(DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),	\
218 						display_timings),  hsync_active) ?		\
219 						kDSI_DpiHsyncActiveHigh :			\
220 						kDSI_DpiHsyncActiveLow),			\
221 			.hfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),			\
222 						display_timings),  hfront_porch),		\
223 			.hbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),			\
224 						display_timings),  hback_porch),		\
225 			.hsw = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),			\
226 						display_timings),  hsync_len),			\
227 			.vfp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),			\
228 						display_timings),  vfront_porch),		\
229 			.vbp = DT_PROP(DT_CHILD(DT_INST_PHANDLE(id, nxp_lcdif),			\
230 						display_timings),  vback_porch),		\
231 		},										\
232 		.auto_insert_eotp = DT_INST_PROP(id, autoinsert_eotp),				\
233 		.dphy_ref_freq = DT_INST_PROP_OR(id, dphy_ref_frequency, 0),			\
234 		.bit_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, dphy)),		\
235 		.bit_clk_subsys =								\
236 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, dphy, name),	\
237 		.esc_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, esc)),		\
238 		.esc_clk_subsys =								\
239 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, esc, name),	\
240 		.pixel_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(id, pixel)),		\
241 		.pixel_clk_subsys =								\
242 			(clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(id, pixel, name),	\
243 	};											\
244 	DEVICE_DT_INST_DEFINE(id,								\
245 			    &mcux_mipi_dsi_init,						\
246 			    NULL,								\
247 			    NULL,								\
248 			    &mipi_dsi_config_##id,						\
249 			    POST_KERNEL,							\
250 			    CONFIG_MIPI_DSI_INIT_PRIORITY,					\
251 			    &dsi_mcux_api);
252 
253 DT_INST_FOREACH_STATUS_OKAY(MCUX_MIPI_DSI_DEVICE)
254