blob: d273a75419e3a3ac24b2bc71c23434bdf30e86e1 [file] [log] [blame]
Jimmy Zhange3a938d2014-09-15 16:50:36 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Jimmy Zhange3a938d2014-09-15 16:50:36 -070014 */
15/*
16 * MIPI DSI Bus
17 *
18 * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
19 * Andrzej Hajda <a.hajda@samsung.com>
20 *
21 * Permission is hereby granted, free of charge, to any person obtaining a
22 * copy of this software and associated documentation files (the
23 * "Software"), to deal in the Software without restriction, including
24 * without limitation the rights to use, copy, modify, merge, publish,
25 * distribute, sub license, and/or sell copies of the Software, and to
26 * permit persons to whom the Software is furnished to do so, subject to
27 * the following conditions:
28 *
29 * The above copyright notice and this permission notice (including the
30 * next paragraph) shall be included in all copies or substantial portions
31 * of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
36 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
37 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
38 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
39 * USE OR OTHER DEALINGS IN THE SOFTWARE.
40 */
41#include <console/console.h>
42#include <arch/io.h>
43#include <stdint.h>
44#include <lib.h>
45#include <stdlib.h>
46#include <delay.h>
47#include <string.h>
48#include <soc/addressmap.h>
49#include <soc/clock.h>
50#include <device/device.h>
51#include <soc/nvidia/tegra/types.h>
52#include <soc/display.h>
53#include <soc/mipi_dsi.h>
54#include <soc/mipi_display.h>
55#include <soc/tegra_dsi.h>
56
57struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = {
58 {
59 .master = NULL,
60 .slave = &mipi_dsi_device_data[DSI_B],
61 },
62 {
63 .master = &mipi_dsi_device_data[DSI_A],
64 .slave = NULL,
65 },
66};
67
68static struct mipi_dsi_device *
69mipi_dsi_device_alloc(struct mipi_dsi_host *host)
70{
71 static int index = 0;
72 struct mipi_dsi_device *dsi;
73
74 if (index >= NUM_DSI)
75 return (void *)-EPTR;
76
77 dsi = &mipi_dsi_device_data[index++];
78 dsi->host = host;
79 return dsi;
80}
81
82static struct mipi_dsi_device *
83of_mipi_dsi_device_add(struct mipi_dsi_host *host)
84{
85 struct mipi_dsi_device *dsi;
86 u32 reg = 0;
87
88 dsi = mipi_dsi_device_alloc(host);
89 if (IS_ERR_PTR(dsi)) {
90 printk(BIOS_ERR, "failed to allocate DSI device\n");
91 return dsi;
92 }
93
94 dsi->channel = reg;
95 host->dev = (void *)dsi;
96
97 return dsi;
98}
99
100int mipi_dsi_host_register(struct mipi_dsi_host *host)
101{
102 of_mipi_dsi_device_add(host);
103 return 0;
104}
105
106/**
107 * mipi_dsi_attach - attach a DSI device to its DSI host
Martin Roth49348182016-07-06 10:17:19 -0600108 * @param dsi: DSI peripheral
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700109 */
110int mipi_dsi_attach(struct mipi_dsi_device *dsi)
111{
112 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
113
114 if (!ops || !ops->attach)
115 return -ENOSYS;
116
117 return ops->attach(dsi->host, dsi);
118}
119
120/**
121 * mipi_dsi_detach - detach a DSI device from its DSI host
Martin Roth49348182016-07-06 10:17:19 -0600122 * @param dsi: DSI peripheral
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700123 */
124int mipi_dsi_detach(struct mipi_dsi_device *dsi)
125{
126 const struct mipi_dsi_host_ops *ops = dsi->host->ops;
127
128 if (!ops || !ops->detach)
129 return -ENOSYS;
130
131 return ops->detach(dsi->host, dsi);
132}
133
134/**
135 * mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel
136 * operation
Martin Roth49348182016-07-06 10:17:19 -0600137 * @param master: master DSI peripheral device
138 * @param slave: slave DSI peripheral device
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700139 *
Martin Roth49348182016-07-06 10:17:19 -0600140 * @return 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700141 */
142int mipi_dsi_enslave(struct mipi_dsi_device *master,
143 struct mipi_dsi_device *slave)
144{
145 int err = 0;
146
147 slave->master = master;
148 master->slave = slave;
149
150 if (master->ops && master->ops->enslave)
151 err = master->ops->enslave(master, slave);
152
153 return err;
154}
155
156/**
157 * mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual-
158 * channel operation
Martin Roth49348182016-07-06 10:17:19 -0600159 * @param master: master DSI peripheral device
160 * @param slave: slave DSI peripheral device
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700161 *
Martin Roth49348182016-07-06 10:17:19 -0600162 * @return 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700163 */
164int mipi_dsi_liberate(struct mipi_dsi_device *master,
165 struct mipi_dsi_device *slave)
166{
167 int err = 0;
168
169 if (master->ops && master->ops->liberate)
170 err = master->ops->liberate(master, slave);
171
172 master->slave = NULL;
173 slave->master = NULL;
174
175 return err;
176}
177
178/**
179 * mipi_dsi_dcs_write() - send DCS write command
Martin Roth49348182016-07-06 10:17:19 -0600180 * @param dsi: DSI peripheral device
181 * @param cmd: DCS command
182 * @param data: buffer containing the command payload
183 * @param len: command payload length
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700184 *
185 * This function will automatically choose the right data type depending on
186 * the command payload length.
187 *
Martin Roth49348182016-07-06 10:17:19 -0600188 * @return The number of bytes successfully transmitted or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700189 */
190ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
191 const void *data, size_t len)
192{
193 struct mipi_dsi_msg msg;
194 ssize_t err;
195 size_t size;
196
197 u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4];
198 u8 *tx = buffer;
199
200 if (len > MAX_DSI_HOST_FIFO_DEPTH) {
201 printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n",
202 __func__, len);
203
204 return -EINVAL;
205 }
206
207 if (len > 0) {
208 unsigned int offset = 0;
209
210 /*
211 * DCS long write packets contain the word count in the header
212 * bytes 1 and 2 and have a payload containing the DCS command
213 * byte folowed by word count minus one bytes.
214 *
215 * DCS short write packets encode the DCS command and up to
216 * one parameter in header bytes 1 and 2.
217 */
218 if (len > 1)
219 size = 3 + len;
220 else
221 size = 1 + len;
222
223 /* write word count to header for DCS long write packets */
224 if (len > 1) {
225 tx[offset++] = ((1 + len) >> 0) & 0xff;
226 tx[offset++] = ((1 + len) >> 8) & 0xff;
227 }
228
229 /* write the DCS command byte followed by the payload */
230 tx[offset++] = cmd;
231 memcpy(tx + offset, data, len);
232 } else {
233 tx = &cmd;
234 size = 1;
235 }
236
237 memset(&msg, 0, sizeof(msg));
238 msg.flags = MIPI_DSI_MSG_USE_LPM;
239 msg.channel = dsi->channel;
240 msg.tx_len = size;
241 msg.tx_buf = tx;
242
243 switch (len) {
244 case 0:
245 msg.type = MIPI_DSI_DCS_SHORT_WRITE;
246 break;
247 case 1:
248 msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
249 break;
250 default:
251 msg.type = MIPI_DSI_DCS_LONG_WRITE;
252 break;
253 }
254
255 err = dsi->host->ops->transfer(dsi->host, &msg);
256
257 return err;
258}
259
260/**
261 * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
262 * module
Martin Roth49348182016-07-06 10:17:19 -0600263 * @param dsi: DSI peripheral device
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700264 *
Martin Roth49348182016-07-06 10:17:19 -0600265 * @return 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700266 */
267int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
268{
269 ssize_t err;
270
271 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
272 if (err < 0)
273 return err;
274
275 return 0;
276}
277
278/**
279 * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
280 * display device
Martin Roth49348182016-07-06 10:17:19 -0600281 * @param dsi: DSI peripheral device
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700282 *
Martin Roth49348182016-07-06 10:17:19 -0600283 * @return 0 on success or a negative error code on failure
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700284 */
285int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
286{
287 ssize_t err;
288
289 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
290 if (err < 0)
291 return err;
292
293 return 0;
294}
295
296/**
297 * mipi_dsi_dcs_set_column_address() - define the column extent of the frame
298 * memory accessed by the host processor
Martin Roth49348182016-07-06 10:17:19 -0600299 * @param dsi: DSI peripheral device
300 * @param start: first column of frame memory
301 * @param end: last column of frame memory
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700302 *
Martin Roth49348182016-07-06 10:17:19 -0600303 * @return 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700304 */
305int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
306 u16 end)
307{
308 u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
309 ssize_t err;
310
311 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
312 sizeof(payload));
313 if (err < 0)
314 return err;
315
316 return 0;
317}
318
319/**
320 * mipi_dsi_dcs_set_page_address() - define the page extent of the frame
321 * memory accessed by the host processor
Martin Roth49348182016-07-06 10:17:19 -0600322 * @param dsi: DSI peripheral device
323 * @param start: first page of frame memory
324 * @param end: last page of frame memory
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700325 *
Martin Roth49348182016-07-06 10:17:19 -0600326 * @return: 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700327 */
328int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
329 u16 end)
330{
331 u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
332 ssize_t err;
333
334 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
335 sizeof(payload));
336 if (err < 0)
337 return err;
338
339 return 0;
340}
341
342/**
343 * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
344 * output signal on the TE signal line.
Martin Roth49348182016-07-06 10:17:19 -0600345 * @param dsi: DSI peripheral device
346 * @param mode: the Tearing Effect Output Line mode
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700347 *
Martin Roth49348182016-07-06 10:17:19 -0600348 * @return 0 on success or a negative error code on failure
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700349 */
350int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
351 enum mipi_dsi_dcs_tear_mode mode)
352{
353 u8 value = mode;
354 ssize_t err;
355
356 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
357 sizeof(value));
358 if (err < 0)
359 return err;
360
361 return 0;
362}
363
364/**
Martin Roth49348182016-07-06 10:17:19 -0600365 * @brief mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700366 * data used by the interface
Martin Roth49348182016-07-06 10:17:19 -0600367 * @param dsi: DSI peripheral device
368 * @param format: pixel format
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700369 *
Martin Roth49348182016-07-06 10:17:19 -0600370 * @return 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700371 */
372int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
373{
374 ssize_t err;
375
376 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
377 sizeof(format));
378 if (err < 0)
379 return err;
380
381 return 0;
382}
383
384/**
385 * mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers
386 * from the host to the peripheral
Martin Roth49348182016-07-06 10:17:19 -0600387 * @param dsi: DSI peripheral device
388 * @param reverse_page_address: reverses the page addressing to bottom->top
389 * @param reverse_col_address: reverses the column addressing to right->left
390 * @param reverse_page_col_address: reverses the page/column addressing order
391 * @param refresh_from_bottom: refresh the display bottom to top
392 * @param reverse_rgb: send pixel data bgr instead of rgb
393 * @param latch_right_to_left: latch the incoming display data right to left
394 * @param flip_horizontal: flip the image horizontally, left to right
395 * @param flip_vertical: flip the image vertically, top to bottom
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700396 *
Martin Roth49348182016-07-06 10:17:19 -0600397 * @return 0 on success or a negative error code on failure.
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700398 */
399int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
400 bool reverse_page_address,
401 bool reverse_col_address,
402 bool reverse_page_col_address,
403 bool refresh_from_bottom,
404 bool reverse_rgb,
405 bool latch_right_to_left,
406 bool flip_horizontal,
407 bool flip_vertical)
408{
409 ssize_t err;
410 u8 data;
411
412 data = ((flip_vertical ? 1 : 0) << 0) |
413 ((flip_horizontal ? 1 : 0) << 1) |
414 ((latch_right_to_left ? 1 : 0) << 2) |
415 ((reverse_rgb ? 1 : 0) << 3) |
416 ((refresh_from_bottom ? 1 : 0) << 4) |
417 ((reverse_page_col_address ? 1 : 0) << 5) |
418 ((reverse_col_address ? 1 : 0) << 6) |
419 ((reverse_page_address ? 1 : 0) << 7);
420
421 err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1);
422 if (err < 0)
423 return err;
424
425 return 0;
426}