blob: d273a75419e3a3ac24b2bc71c23434bdf30e86e1 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright 2014 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* MIPI DSI Bus
*
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
* Andrzej Hajda <a.hajda@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <console/console.h>
#include <arch/io.h>
#include <stdint.h>
#include <lib.h>
#include <stdlib.h>
#include <delay.h>
#include <string.h>
#include <soc/addressmap.h>
#include <soc/clock.h>
#include <device/device.h>
#include <soc/nvidia/tegra/types.h>
#include <soc/display.h>
#include <soc/mipi_dsi.h>
#include <soc/mipi_display.h>
#include <soc/tegra_dsi.h>
struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = {
{
.master = NULL,
.slave = &mipi_dsi_device_data[DSI_B],
},
{
.master = &mipi_dsi_device_data[DSI_A],
.slave = NULL,
},
};
static struct mipi_dsi_device *
mipi_dsi_device_alloc(struct mipi_dsi_host *host)
{
static int index = 0;
struct mipi_dsi_device *dsi;
if (index >= NUM_DSI)
return (void *)-EPTR;
dsi = &mipi_dsi_device_data[index++];
dsi->host = host;
return dsi;
}
static struct mipi_dsi_device *
of_mipi_dsi_device_add(struct mipi_dsi_host *host)
{
struct mipi_dsi_device *dsi;
u32 reg = 0;
dsi = mipi_dsi_device_alloc(host);
if (IS_ERR_PTR(dsi)) {
printk(BIOS_ERR, "failed to allocate DSI device\n");
return dsi;
}
dsi->channel = reg;
host->dev = (void *)dsi;
return dsi;
}
int mipi_dsi_host_register(struct mipi_dsi_host *host)
{
of_mipi_dsi_device_add(host);
return 0;
}
/**
* mipi_dsi_attach - attach a DSI device to its DSI host
* @param dsi: DSI peripheral
*/
int mipi_dsi_attach(struct mipi_dsi_device *dsi)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->attach)
return -ENOSYS;
return ops->attach(dsi->host, dsi);
}
/**
* mipi_dsi_detach - detach a DSI device from its DSI host
* @param dsi: DSI peripheral
*/
int mipi_dsi_detach(struct mipi_dsi_device *dsi)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->detach)
return -ENOSYS;
return ops->detach(dsi->host, dsi);
}
/**
* mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel
* operation
* @param master: master DSI peripheral device
* @param slave: slave DSI peripheral device
*
* @return 0 on success or a negative error code on failure.
*/
int mipi_dsi_enslave(struct mipi_dsi_device *master,
struct mipi_dsi_device *slave)
{
int err = 0;
slave->master = master;
master->slave = slave;
if (master->ops && master->ops->enslave)
err = master->ops->enslave(master, slave);
return err;
}
/**
* mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual-
* channel operation
* @param master: master DSI peripheral device
* @param slave: slave DSI peripheral device
*
* @return 0 on success or a negative error code on failure.
*/
int mipi_dsi_liberate(struct mipi_dsi_device *master,
struct mipi_dsi_device *slave)
{
int err = 0;
if (master->ops && master->ops->liberate)
err = master->ops->liberate(master, slave);
master->slave = NULL;
slave->master = NULL;
return err;
}
/**
* mipi_dsi_dcs_write() - send DCS write command
* @param dsi: DSI peripheral device
* @param cmd: DCS command
* @param data: buffer containing the command payload
* @param len: command payload length
*
* This function will automatically choose the right data type depending on
* the command payload length.
*
* @return The number of bytes successfully transmitted or a negative error code on failure.
*/
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
const void *data, size_t len)
{
struct mipi_dsi_msg msg;
ssize_t err;
size_t size;
u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4];
u8 *tx = buffer;
if (len > MAX_DSI_HOST_FIFO_DEPTH) {
printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n",
__func__, len);
return -EINVAL;
}
if (len > 0) {
unsigned int offset = 0;
/*
* DCS long write packets contain the word count in the header
* bytes 1 and 2 and have a payload containing the DCS command
* byte folowed by word count minus one bytes.
*
* DCS short write packets encode the DCS command and up to
* one parameter in header bytes 1 and 2.
*/
if (len > 1)
size = 3 + len;
else
size = 1 + len;
/* write word count to header for DCS long write packets */
if (len > 1) {
tx[offset++] = ((1 + len) >> 0) & 0xff;
tx[offset++] = ((1 + len) >> 8) & 0xff;
}
/* write the DCS command byte followed by the payload */
tx[offset++] = cmd;
memcpy(tx + offset, data, len);
} else {
tx = &cmd;
size = 1;
}
memset(&msg, 0, sizeof(msg));
msg.flags = MIPI_DSI_MSG_USE_LPM;
msg.channel = dsi->channel;
msg.tx_len = size;
msg.tx_buf = tx;
switch (len) {
case 0:
msg.type = MIPI_DSI_DCS_SHORT_WRITE;
break;
case 1:
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
break;
default:
msg.type = MIPI_DSI_DCS_LONG_WRITE;
break;
}
err = dsi->host->ops->transfer(dsi->host, &msg);
return err;
}
/**
* mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
* module
* @param dsi: DSI peripheral device
*
* @return 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_display_on() - start displaying the image data on the
* display device
* @param dsi: DSI peripheral device
*
* @return 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_column_address() - define the column extent of the frame
* memory accessed by the host processor
* @param dsi: DSI peripheral device
* @param start: first column of frame memory
* @param end: last column of frame memory
*
* @return 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
u16 end)
{
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
sizeof(payload));
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_page_address() - define the page extent of the frame
* memory accessed by the host processor
* @param dsi: DSI peripheral device
* @param start: first page of frame memory
* @param end: last page of frame memory
*
* @return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
u16 end)
{
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
sizeof(payload));
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
* output signal on the TE signal line.
* @param dsi: DSI peripheral device
* @param mode: the Tearing Effect Output Line mode
*
* @return 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
enum mipi_dsi_dcs_tear_mode mode)
{
u8 value = mode;
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
sizeof(value));
if (err < 0)
return err;
return 0;
}
/**
* @brief mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
* data used by the interface
* @param dsi: DSI peripheral device
* @param format: pixel format
*
* @return 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
sizeof(format));
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers
* from the host to the peripheral
* @param dsi: DSI peripheral device
* @param reverse_page_address: reverses the page addressing to bottom->top
* @param reverse_col_address: reverses the column addressing to right->left
* @param reverse_page_col_address: reverses the page/column addressing order
* @param refresh_from_bottom: refresh the display bottom to top
* @param reverse_rgb: send pixel data bgr instead of rgb
* @param latch_right_to_left: latch the incoming display data right to left
* @param flip_horizontal: flip the image horizontally, left to right
* @param flip_vertical: flip the image vertically, top to bottom
*
* @return 0 on success or a negative error code on failure.
*/
int 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)
{
ssize_t err;
u8 data;
data = ((flip_vertical ? 1 : 0) << 0) |
((flip_horizontal ? 1 : 0) << 1) |
((latch_right_to_left ? 1 : 0) << 2) |
((reverse_rgb ? 1 : 0) << 3) |
((refresh_from_bottom ? 1 : 0) << 4) |
((reverse_page_col_address ? 1 : 0) << 5) |
((reverse_col_address ? 1 : 0) << 6) |
((reverse_page_address ? 1 : 0) << 7);
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1);
if (err < 0)
return err;
return 0;
}