xref: /aosp_15_r20/external/coreboot/src/soc/nvidia/tegra210/mipi_dsi.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * MIPI DSI Bus
4  *
5  * Andrzej Hajda <[email protected]>
6  *
7  */
8 
9 #include <console/console.h>
10 #include <string.h>
11 #include <soc/addressmap.h>
12 #include <soc/clock.h>
13 #include <device/device.h>
14 #include <soc/nvidia/tegra/types.h>
15 #include <soc/display.h>
16 #include <soc/mipi_dsi.h>
17 #include <soc/mipi_display.h>
18 #include <soc/tegra_dsi.h>
19 #include <types.h>
20 
21 struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = {
22 	{
23 		.master = NULL,
24 		.slave = &mipi_dsi_device_data[DSI_B],
25 	},
26 	{
27 		.master = &mipi_dsi_device_data[DSI_A],
28 		.slave = NULL,
29 	},
30 };
31 
32 static struct mipi_dsi_device *
mipi_dsi_device_alloc(struct mipi_dsi_host * host)33 mipi_dsi_device_alloc(struct mipi_dsi_host *host)
34 {
35 	static int index = 0;
36 	struct mipi_dsi_device *dsi;
37 
38 	if (index >= NUM_DSI)
39 		return (void *)-EPTR;
40 
41 	dsi = &mipi_dsi_device_data[index++];
42 	dsi->host = host;
43 	return dsi;
44 }
45 
46 static struct mipi_dsi_device *
of_mipi_dsi_device_add(struct mipi_dsi_host * host)47 of_mipi_dsi_device_add(struct mipi_dsi_host *host)
48 {
49 	struct mipi_dsi_device *dsi;
50 	u32 reg = 0;
51 
52 	dsi = mipi_dsi_device_alloc(host);
53 	if (IS_ERR_PTR(dsi)) {
54 		printk(BIOS_ERR, "failed to allocate DSI device\n");
55 		return dsi;
56 	}
57 
58 	dsi->channel = reg;
59 	host->dev = (void *)dsi;
60 
61 	return dsi;
62 }
63 
mipi_dsi_host_register(struct mipi_dsi_host * host)64 int mipi_dsi_host_register(struct mipi_dsi_host *host)
65 {
66 	of_mipi_dsi_device_add(host);
67 	return 0;
68 }
69 
70 /**
71  * mipi_dsi_attach - attach a DSI device to its DSI host
72  * @param dsi: DSI peripheral
73  */
mipi_dsi_attach(struct mipi_dsi_device * dsi)74 int mipi_dsi_attach(struct mipi_dsi_device *dsi)
75 {
76 	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
77 
78 	if (!ops || !ops->attach)
79 		return -ENOSYS;
80 
81 	return ops->attach(dsi->host, dsi);
82 }
83 
84 /**
85  * mipi_dsi_detach - detach a DSI device from its DSI host
86  * @param dsi: DSI peripheral
87  */
mipi_dsi_detach(struct mipi_dsi_device * dsi)88 int mipi_dsi_detach(struct mipi_dsi_device *dsi)
89 {
90 	const struct mipi_dsi_host_ops *ops = dsi->host->ops;
91 
92 	if (!ops || !ops->detach)
93 		return -ENOSYS;
94 
95 	return ops->detach(dsi->host, dsi);
96 }
97 
98 /**
99  * mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel
100  *    operation
101  * @param master: master DSI peripheral device
102  * @param slave: slave DSI peripheral device
103  *
104  * @return 0 on success or a negative error code on failure.
105  */
mipi_dsi_enslave(struct mipi_dsi_device * master,struct mipi_dsi_device * slave)106 int mipi_dsi_enslave(struct mipi_dsi_device *master,
107 		     struct mipi_dsi_device *slave)
108 {
109 	int err = 0;
110 
111 	slave->master = master;
112 	master->slave = slave;
113 
114 	if (master->ops && master->ops->enslave)
115 		err = master->ops->enslave(master, slave);
116 
117 	return err;
118 }
119 
120 /**
121  * mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual-
122  *    channel operation
123  * @param master: master DSI peripheral device
124  * @param slave: slave DSI peripheral device
125  *
126  * @return 0 on success or a negative error code on failure.
127  */
mipi_dsi_liberate(struct mipi_dsi_device * master,struct mipi_dsi_device * slave)128 int mipi_dsi_liberate(struct mipi_dsi_device *master,
129 		      struct mipi_dsi_device *slave)
130 {
131 	int err = 0;
132 
133 	if (master->ops && master->ops->liberate)
134 		err = master->ops->liberate(master, slave);
135 
136 	master->slave = NULL;
137 	slave->master = NULL;
138 
139 	return err;
140 }
141 
142 /**
143  * mipi_dsi_dcs_write() - send DCS write command
144  * @param dsi: DSI peripheral device
145  * @param cmd: DCS command
146  * @param data: buffer containing the command payload
147  * @param len: command payload length
148  *
149  * This function will automatically choose the right data type depending on
150  * the command payload length.
151  *
152  * @return The number of bytes successfully transmitted or a negative error code on failure.
153  */
mipi_dsi_dcs_write(struct mipi_dsi_device * dsi,u8 cmd,const void * data,size_t len)154 ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
155 			   const void *data, size_t len)
156 {
157 	struct mipi_dsi_msg msg;
158 	ssize_t err;
159 	size_t size;
160 
161 	u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4];
162 	u8 *tx = buffer;
163 
164 	if (len > MAX_DSI_HOST_FIFO_DEPTH) {
165 		printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n",
166 			__func__, len);
167 
168 		return -EINVAL;
169 	}
170 
171 	if (len > 0) {
172 		unsigned int offset = 0;
173 
174 		/*
175 		 * DCS long write packets contain the word count in the header
176 		 * bytes 1 and 2 and have a payload containing the DCS command
177 		 * byte followed by word count minus one bytes.
178 		 *
179 		 * DCS short write packets encode the DCS command and up to
180 		 * one parameter in header bytes 1 and 2.
181 		 */
182 		if (len > 1)
183 			size = 3 + len;
184 		else
185 			size = 1 + len;
186 
187 		/* write word count to header for DCS long write packets */
188 		if (len > 1) {
189 			tx[offset++] = ((1 + len) >> 0) & 0xff;
190 			tx[offset++] = ((1 + len) >> 8) & 0xff;
191 		}
192 
193 		/* write the DCS command byte followed by the payload */
194 		tx[offset++] = cmd;
195 		memcpy(tx + offset, data, len);
196 	} else {
197 		tx = &cmd;
198 		size = 1;
199 	}
200 
201 	memset(&msg, 0, sizeof(msg));
202 	msg.flags = MIPI_DSI_MSG_USE_LPM;
203 	msg.channel = dsi->channel;
204 	msg.tx_len = size;
205 	msg.tx_buf = tx;
206 
207 	switch (len) {
208 	case 0:
209 		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
210 		break;
211 	case 1:
212 		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
213 		break;
214 	default:
215 		msg.type = MIPI_DSI_DCS_LONG_WRITE;
216 		break;
217 	}
218 
219 	err = dsi->host->ops->transfer(dsi->host, &msg);
220 
221 	return err;
222 }
223 
224 /**
225  * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
226  *    module
227  * @param dsi: DSI peripheral device
228  *
229  * @return 0 on success or a negative error code on failure.
230  */
mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device * dsi)231 int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
232 {
233 	ssize_t err;
234 
235 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
236 	if (err < 0)
237 		return err;
238 
239 	return 0;
240 }
241 
242 /**
243  * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
244  *    display device
245  * @param dsi: DSI peripheral device
246  *
247  * @return 0 on success or a negative error code on failure
248  */
mipi_dsi_dcs_set_display_on(struct mipi_dsi_device * dsi)249 int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
250 {
251 	ssize_t err;
252 
253 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
254 	if (err < 0)
255 		return err;
256 
257 	return 0;
258 }
259 
260 /**
261  * mipi_dsi_dcs_set_column_address() - define the column extent of the frame
262  *    memory accessed by the host processor
263  * @param dsi: DSI peripheral device
264  * @param start: first column of frame memory
265  * @param end: last column of frame memory
266  *
267  * @return 0 on success or a negative error code on failure.
268  */
mipi_dsi_dcs_set_column_address(struct mipi_dsi_device * dsi,u16 start,u16 end)269 int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
270 				    u16 end)
271 {
272 	u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
273 	ssize_t err;
274 
275 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
276 				 sizeof(payload));
277 	if (err < 0)
278 		return err;
279 
280 	return 0;
281 }
282 
283 /**
284  * mipi_dsi_dcs_set_page_address() - define the page extent of the frame
285  *    memory accessed by the host processor
286  * @param dsi: DSI peripheral device
287  * @param start: first page of frame memory
288  * @param end: last page of frame memory
289  *
290  * @return 0 on success or a negative error code on failure.
291  */
mipi_dsi_dcs_set_page_address(struct mipi_dsi_device * dsi,u16 start,u16 end)292 int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
293 				  u16 end)
294 {
295 	u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
296 	ssize_t err;
297 
298 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
299 				 sizeof(payload));
300 	if (err < 0)
301 		return err;
302 
303 	return 0;
304 }
305 
306 /**
307  * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
308  *    output signal on the TE signal line.
309  * @param dsi: DSI peripheral device
310  * @param mode: the Tearing Effect Output Line mode
311  *
312  * @return 0 on success or a negative error code on failure
313  */
mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device * dsi,enum mipi_dsi_dcs_tear_mode mode)314 int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
315 			     enum mipi_dsi_dcs_tear_mode mode)
316 {
317 	u8 value = mode;
318 	ssize_t err;
319 
320 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
321 				 sizeof(value));
322 	if (err < 0)
323 		return err;
324 
325 	return 0;
326 }
327 
328 /**
329  * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
330  *    data used by the interface
331  * @param dsi: DSI peripheral device
332  * @param format: pixel format
333  *
334  * @return 0 on success or a negative error code on failure.
335  */
mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device * dsi,u8 format)336 int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
337 {
338 	ssize_t err;
339 
340 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
341 				 sizeof(format));
342 	if (err < 0)
343 		return err;
344 
345 	return 0;
346 }
347 
348 /**
349  * mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers
350  *    from the host to the peripheral
351  * @param dsi: DSI peripheral device
352  * @param reverse_page_address: reverses the page addressing to bottom->top
353  * @param reverse_col_address: reverses the column addressing to right->left
354  * @param reverse_page_col_address: reverses the page/column addressing order
355  * @param refresh_from_bottom: refresh the display bottom to top
356  * @param reverse_rgb: send pixel data bgr instead of rgb
357  * @param latch_right_to_left: latch the incoming display data right to left
358  * @param flip_horizontal: flip the image horizontally, left to right
359  * @param flip_vertical: flip the image vertically, top to bottom
360  *
361  * @return 0 on success or a negative error code on failure.
362  */
mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device * dsi,bool reverse_page_address,bool reverse_col_address,bool reverse_page_col_address,bool refresh_from_bottom,bool reverse_rgb,bool latch_right_to_left,bool flip_horizontal,bool flip_vertical)363 int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
364 			bool reverse_page_address,
365 			bool reverse_col_address,
366 			bool reverse_page_col_address,
367 			bool refresh_from_bottom,
368 			bool reverse_rgb,
369 			bool latch_right_to_left,
370 			bool flip_horizontal,
371 			bool flip_vertical)
372 {
373 	ssize_t err;
374 	u8 data;
375 
376 	data = ((flip_vertical ? 1 : 0) << 0) |
377 		((flip_horizontal ? 1 : 0) << 1) |
378 		((latch_right_to_left ? 1 : 0) << 2) |
379 		((reverse_rgb ? 1 : 0) << 3) |
380 		((refresh_from_bottom ? 1 : 0) << 4) |
381 		((reverse_page_col_address ? 1 : 0) << 5) |
382 		((reverse_col_address ? 1 : 0) << 6) |
383 		((reverse_page_address ? 1 : 0) << 7);
384 
385 	err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1);
386 	if (err < 0)
387 		return err;
388 
389 	return 0;
390 }
391