blob: 067dc6d6187484cba33253242b9283f2e5e236f3 [file] [log] [blame]
Angel Ponsa2ee7612020-04-04 18:51:15 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Patrick Georgi40a3e322015-06-22 19:41:29 +02002/*
3 * MIPI DSI Bus
4 *
Patrick Georgi40a3e322015-06-22 19:41:29 +02005 * Andrzej Hajda <a.hajda@samsung.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
Elyes HAOUASbf0970e2019-03-21 11:10:03 +010027
Patrick Georgi40a3e322015-06-22 19:41:29 +020028#include <console/console.h>
Patrick Georgi40a3e322015-06-22 19:41:29 +020029#include <string.h>
30#include <soc/addressmap.h>
31#include <soc/clock.h>
32#include <device/device.h>
33#include <soc/nvidia/tegra/types.h>
34#include <soc/display.h>
35#include <soc/mipi_dsi.h>
36#include <soc/mipi_display.h>
37#include <soc/tegra_dsi.h>
Elyes HAOUAS30818552019-06-23 07:03:59 +020038#include <types.h>
Patrick Georgi40a3e322015-06-22 19:41:29 +020039
40struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = {
41 {
42 .master = NULL,
43 .slave = &mipi_dsi_device_data[DSI_B],
44 },
45 {
46 .master = &mipi_dsi_device_data[DSI_A],
47 .slave = NULL,
48 },
49};
50
51static struct mipi_dsi_device *
52mipi_dsi_device_alloc(struct mipi_dsi_host *host)
53{
54 static int index = 0;
55 struct mipi_dsi_device *dsi;
56
57 if (index >= NUM_DSI)
58 return (void *)-EPTR;
59
60 dsi = &mipi_dsi_device_data[index++];
61 dsi->host = host;
62 return dsi;
63}
64
65static struct mipi_dsi_device *
66of_mipi_dsi_device_add(struct mipi_dsi_host *host)
67{
68 struct mipi_dsi_device *dsi;
69 u32 reg = 0;
70
71 dsi = mipi_dsi_device_alloc(host);
72 if (IS_ERR_PTR(dsi)) {
73 printk(BIOS_ERR, "failed to allocate DSI device\n");
74 return dsi;
75 }
76
77 dsi->channel = reg;
78 host->dev = (void *)dsi;
79
80 return dsi;
81}
82
83int mipi_dsi_host_register(struct mipi_dsi_host *host)
84{
85 of_mipi_dsi_device_add(host);
86 return 0;
87}
88
89/**
90 * mipi_dsi_attach - attach a DSI device to its DSI host
Martin Roth49348182016-07-06 10:17:19 -060091 * @param dsi: DSI peripheral
Patrick Georgi40a3e322015-06-22 19:41:29 +020092 */
93int mipi_dsi_attach(struct mipi_dsi_device *dsi)
94{
95 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
96
97 if (!ops || !ops->attach)
98 return -ENOSYS;
99
100 return ops->attach(dsi->host, dsi);
101}
102
103/**
104 * mipi_dsi_detach - detach a DSI device from its DSI host
Martin Roth49348182016-07-06 10:17:19 -0600105 * @param dsi: DSI peripheral
Patrick Georgi40a3e322015-06-22 19:41:29 +0200106 */
107int mipi_dsi_detach(struct mipi_dsi_device *dsi)
108{
109 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
110
111 if (!ops || !ops->detach)
112 return -ENOSYS;
113
114 return ops->detach(dsi->host, dsi);
115}
116
117/**
118 * mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel
119 * operation
Martin Roth49348182016-07-06 10:17:19 -0600120 * @param master: master DSI peripheral device
121 * @param slave: slave DSI peripheral device
Patrick Georgi40a3e322015-06-22 19:41:29 +0200122 *
Martin Roth49348182016-07-06 10:17:19 -0600123 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200124 */
125int mipi_dsi_enslave(struct mipi_dsi_device *master,
126 struct mipi_dsi_device *slave)
127{
128 int err = 0;
129
130 slave->master = master;
131 master->slave = slave;
132
133 if (master->ops && master->ops->enslave)
134 err = master->ops->enslave(master, slave);
135
136 return err;
137}
138
139/**
140 * mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual-
141 * channel operation
Martin Roth49348182016-07-06 10:17:19 -0600142 * @param master: master DSI peripheral device
143 * @param slave: slave DSI peripheral device
Patrick Georgi40a3e322015-06-22 19:41:29 +0200144 *
Martin Roth49348182016-07-06 10:17:19 -0600145 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200146 */
147int mipi_dsi_liberate(struct mipi_dsi_device *master,
148 struct mipi_dsi_device *slave)
149{
150 int err = 0;
151
152 if (master->ops && master->ops->liberate)
153 err = master->ops->liberate(master, slave);
154
155 master->slave = NULL;
156 slave->master = NULL;
157
158 return err;
159}
160
161/**
162 * mipi_dsi_dcs_write() - send DCS write command
Martin Roth49348182016-07-06 10:17:19 -0600163 * @param dsi: DSI peripheral device
164 * @param cmd: DCS command
165 * @param data: buffer containing the command payload
166 * @param len: command payload length
Patrick Georgi40a3e322015-06-22 19:41:29 +0200167 *
168 * This function will automatically choose the right data type depending on
169 * the command payload length.
170 *
Martin Roth49348182016-07-06 10:17:19 -0600171 * @return The number of bytes successfully transmitted or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200172 */
173ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
174 const void *data, size_t len)
175{
176 struct mipi_dsi_msg msg;
177 ssize_t err;
178 size_t size;
179
180 u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4];
181 u8 *tx = buffer;
182
183 if (len > MAX_DSI_HOST_FIFO_DEPTH) {
184 printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n",
185 __func__, len);
186
187 return -EINVAL;
188 }
189
190 if (len > 0) {
191 unsigned int offset = 0;
192
193 /*
194 * DCS long write packets contain the word count in the header
195 * bytes 1 and 2 and have a payload containing the DCS command
Martin Roth26f97f92021-10-01 14:53:22 -0600196 * byte followed by word count minus one bytes.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200197 *
198 * DCS short write packets encode the DCS command and up to
199 * one parameter in header bytes 1 and 2.
200 */
201 if (len > 1)
202 size = 3 + len;
203 else
204 size = 1 + len;
205
206 /* write word count to header for DCS long write packets */
207 if (len > 1) {
208 tx[offset++] = ((1 + len) >> 0) & 0xff;
209 tx[offset++] = ((1 + len) >> 8) & 0xff;
210 }
211
212 /* write the DCS command byte followed by the payload */
213 tx[offset++] = cmd;
214 memcpy(tx + offset, data, len);
215 } else {
216 tx = &cmd;
217 size = 1;
218 }
219
220 memset(&msg, 0, sizeof(msg));
221 msg.flags = MIPI_DSI_MSG_USE_LPM;
222 msg.channel = dsi->channel;
223 msg.tx_len = size;
224 msg.tx_buf = tx;
225
226 switch (len) {
227 case 0:
228 msg.type = MIPI_DSI_DCS_SHORT_WRITE;
229 break;
230 case 1:
231 msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
232 break;
233 default:
234 msg.type = MIPI_DSI_DCS_LONG_WRITE;
235 break;
236 }
237
238 err = dsi->host->ops->transfer(dsi->host, &msg);
239
240 return err;
241}
242
243/**
244 * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
245 * module
Martin Roth49348182016-07-06 10:17:19 -0600246 * @param dsi: DSI peripheral device
Patrick Georgi40a3e322015-06-22 19:41:29 +0200247 *
Martin Roth49348182016-07-06 10:17:19 -0600248 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200249 */
250int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
251{
252 ssize_t err;
253
254 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
255 if (err < 0)
256 return err;
257
258 return 0;
259}
260
261/**
262 * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
263 * display device
Martin Roth49348182016-07-06 10:17:19 -0600264 * @param dsi: DSI peripheral device
Patrick Georgi40a3e322015-06-22 19:41:29 +0200265 *
Martin Roth49348182016-07-06 10:17:19 -0600266 * @return 0 on success or a negative error code on failure
Patrick Georgi40a3e322015-06-22 19:41:29 +0200267 */
268int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
269{
270 ssize_t err;
271
272 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
273 if (err < 0)
274 return err;
275
276 return 0;
277}
278
279/**
280 * mipi_dsi_dcs_set_column_address() - define the column extent of the frame
281 * memory accessed by the host processor
Martin Roth49348182016-07-06 10:17:19 -0600282 * @param dsi: DSI peripheral device
283 * @param start: first column of frame memory
284 * @param end: last column of frame memory
Patrick Georgi40a3e322015-06-22 19:41:29 +0200285 *
Martin Roth49348182016-07-06 10:17:19 -0600286 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200287 */
288int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
289 u16 end)
290{
291 u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
292 ssize_t err;
293
294 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
295 sizeof(payload));
296 if (err < 0)
297 return err;
298
299 return 0;
300}
301
302/**
303 * mipi_dsi_dcs_set_page_address() - define the page extent of the frame
304 * memory accessed by the host processor
Martin Roth49348182016-07-06 10:17:19 -0600305 * @param dsi: DSI peripheral device
306 * @param start: first page of frame memory
307 * @param end: last page of frame memory
Patrick Georgi40a3e322015-06-22 19:41:29 +0200308 *
Martin Roth49348182016-07-06 10:17:19 -0600309 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200310 */
311int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
312 u16 end)
313{
314 u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
315 ssize_t err;
316
317 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
318 sizeof(payload));
319 if (err < 0)
320 return err;
321
322 return 0;
323}
324
325/**
326 * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
327 * output signal on the TE signal line.
Martin Roth49348182016-07-06 10:17:19 -0600328 * @param dsi: DSI peripheral device
329 * @param mode: the Tearing Effect Output Line mode
Patrick Georgi40a3e322015-06-22 19:41:29 +0200330 *
Martin Roth49348182016-07-06 10:17:19 -0600331 * @return 0 on success or a negative error code on failure
Patrick Georgi40a3e322015-06-22 19:41:29 +0200332 */
333int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
334 enum mipi_dsi_dcs_tear_mode mode)
335{
336 u8 value = mode;
337 ssize_t err;
338
339 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
340 sizeof(value));
341 if (err < 0)
342 return err;
343
344 return 0;
345}
346
347/**
348 * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
349 * data used by the interface
Martin Roth49348182016-07-06 10:17:19 -0600350 * @param dsi: DSI peripheral device
351 * @param format: pixel format
Patrick Georgi40a3e322015-06-22 19:41:29 +0200352 *
Martin Roth49348182016-07-06 10:17:19 -0600353 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200354 */
355int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
356{
357 ssize_t err;
358
359 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
360 sizeof(format));
361 if (err < 0)
362 return err;
363
364 return 0;
365}
366
367/**
368 * mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers
369 * from the host to the peripheral
Martin Roth49348182016-07-06 10:17:19 -0600370 * @param dsi: DSI peripheral device
371 * @param reverse_page_address: reverses the page addressing to bottom->top
372 * @param reverse_col_address: reverses the column addressing to right->left
373 * @param reverse_page_col_address: reverses the page/column addressing order
374 * @param refresh_from_bottom: refresh the display bottom to top
375 * @param reverse_rgb: send pixel data bgr instead of rgb
376 * @param latch_right_to_left: latch the incoming display data right to left
377 * @param flip_horizontal: flip the image horizontally, left to right
378 * @param flip_vertical: flip the image vertically, top to bottom
Patrick Georgi40a3e322015-06-22 19:41:29 +0200379 *
Martin Roth49348182016-07-06 10:17:19 -0600380 * @return 0 on success or a negative error code on failure.
Patrick Georgi40a3e322015-06-22 19:41:29 +0200381 */
382int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
383 bool reverse_page_address,
384 bool reverse_col_address,
385 bool reverse_page_col_address,
386 bool refresh_from_bottom,
387 bool reverse_rgb,
388 bool latch_right_to_left,
389 bool flip_horizontal,
390 bool flip_vertical)
391{
392 ssize_t err;
393 u8 data;
394
395 data = ((flip_vertical ? 1 : 0) << 0) |
396 ((flip_horizontal ? 1 : 0) << 1) |
397 ((latch_right_to_left ? 1 : 0) << 2) |
398 ((reverse_rgb ? 1 : 0) << 3) |
399 ((refresh_from_bottom ? 1 : 0) << 4) |
400 ((reverse_page_col_address ? 1 : 0) << 5) |
401 ((reverse_col_address ? 1 : 0) << 6) |
402 ((reverse_page_address ? 1 : 0) << 7);
403
404 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1);
405 if (err < 0)
406 return err;
407
408 return 0;
409}