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