video/drm: Sync MIPI DSI Bus helper from Linux 4.9

This imports drivers/gpu/drm/drm_mipi_dsi.c from Linux kernel 4.9.
It can be very helpful when porting Linux mipi dsi driver to U-Boot.

DSI hosts expose operations which can be used by DSI peripheral drivers
to access associated devices.

Change-Id: Iccfa9d946f33458867f4d4db0ce04aeb1918e855
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi 2019-01-31 10:07:23 +08:00 committed by Kever Yang
parent c5bb939d42
commit 1953e61947
12 changed files with 1120 additions and 606 deletions

View File

@ -13,7 +13,8 @@ menuconfig DRM_ROCKCHIP
config DRM_ROCKCHIP_PANEL
bool "Rockchip Panel Support"
select DRM_ROCKCHIP_MIPI_DSI
depends on DRM_ROCKCHIP
select DRM_MIPI_DSI
config DRM_ROCKCHIP_DW_HDMI
bool "Rockchip specific extensions for Synopsys DW HDMI"
@ -52,13 +53,14 @@ config DRM_ROCKCHIP_INNO_VIDEO_COMBO_PHY
Enable this to support the Rockchip MIPI/LVDS/TTL PHY
with Innosilicon IP block.
config DRM_ROCKCHIP_MIPI_DSI
config DRM_MIPI_DSI
bool
depends on DRM_ROCKCHIP
config DRM_ROCKCHIP_DW_MIPI_DSI
tristate "Rockchip specific extensions for Synopsys DW MIPI DSI"
depends on DRM_ROCKCHIP
select DRM_ROCKCHIP_MIPI_DSI
select DRM_MIPI_DSI
select DRM_ROCKCHIP_PANEL
help
This selects support for Rockchip SoC specific extensions

View File

@ -7,7 +7,7 @@
obj-y += rockchip_display.o rockchip_crtc.o rockchip_phy.o rockchip_bridge.o \
rockchip_vop.o rockchip_vop_reg.o bmp_helper.o
obj-$(CONFIG_DRM_ROCKCHIP_MIPI_DSI) += rockchip_mipi_dsi.o
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
obj-$(CONFIG_DRM_ROCKCHIP_DW_MIPI_DSI) += dw_mipi_dsi.o
obj-$(CONFIG_DRM_ROCKCHIP_DW_HDMI) += rockchip_dw_hdmi.o dw_hdmi.o
obj-$(CONFIG_ROCKCHIP_INNO_HDMI_PHY) += rockchip-inno-hdmi-phy.o

View File

@ -0,0 +1,744 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* MIPI DSI Bus
*
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
* Andrzej Hajda <a.hajda@samsung.com>
*/
#include <drm/drm_mipi_dsi.h>
#include <config.h>
#include <common.h>
#include <errno.h>
#include <malloc.h>
/**
* mipi_dsi_attach - attach a DSI device to its DSI host
* @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
* @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);
}
static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
struct mipi_dsi_msg *msg)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
if (!ops || !ops->transfer)
return -ENOSYS;
if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
msg->flags |= MIPI_DSI_MSG_USE_LPM;
return ops->transfer(dsi->host, msg);
}
/**
* mipi_dsi_packet_format_is_short - check if a packet is of the short format
* @type: MIPI DSI data type of the packet
*
* Return: true if the packet for the given data type is a short packet, false
* otherwise.
*/
bool mipi_dsi_packet_format_is_short(u8 type)
{
switch (type) {
case MIPI_DSI_V_SYNC_START:
case MIPI_DSI_V_SYNC_END:
case MIPI_DSI_H_SYNC_START:
case MIPI_DSI_H_SYNC_END:
case MIPI_DSI_END_OF_TRANSMISSION:
case MIPI_DSI_COLOR_MODE_OFF:
case MIPI_DSI_COLOR_MODE_ON:
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
case MIPI_DSI_TURN_ON_PERIPHERAL:
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_READ:
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
return true;
}
return false;
}
/**
* mipi_dsi_packet_format_is_long - check if a packet is of the long format
* @type: MIPI DSI data type of the packet
*
* Return: true if the packet for the given data type is a long packet, false
* otherwise.
*/
bool mipi_dsi_packet_format_is_long(u8 type)
{
switch (type) {
case MIPI_DSI_NULL_PACKET:
case MIPI_DSI_BLANKING_PACKET:
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
case MIPI_DSI_PACKED_PIXEL_STREAM_30:
case MIPI_DSI_PACKED_PIXEL_STREAM_36:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
return true;
}
return false;
}
/**
* mipi_dsi_create_packet - create a packet from a message according to the
* DSI protocol
* @packet: pointer to a DSI packet structure
* @msg: message to translate into a packet
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg)
{
if (!packet || !msg)
return -EINVAL;
/* do some minimum sanity checking */
if (!mipi_dsi_packet_format_is_short(msg->type) &&
!mipi_dsi_packet_format_is_long(msg->type))
return -EINVAL;
if (msg->channel > 3)
return -EINVAL;
memset(packet, 0, sizeof(*packet));
packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
if (mipi_dsi_packet_format_is_long(msg->type)) {
packet->header[1] = (msg->tx_len >> 0) & 0xff;
packet->header[2] = (msg->tx_len >> 8) & 0xff;
packet->payload_length = msg->tx_len;
packet->payload = msg->tx_buf;
} else {
const u8 *tx = msg->tx_buf;
packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
}
packet->size = sizeof(packet->header) + packet->payload_length;
return 0;
}
/**
* mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SHUTDOWN_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
};
int ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
/**
* mipi_dsi_turn_on_peripheral() - sends a Turn On Peripheral command
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_TURN_ON_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
};
int ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
/*
* mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
* the payload in a long packet transmitted from the peripheral back to the
* host processor
* @dsi: DSI peripheral device
* @value: the maximum size of the payload
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value)
{
u8 tx[2] = { value & 0xff, value >> 8 };
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
.tx_len = sizeof(tx),
.tx_buf = tx,
};
int ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
/**
* mipi_dsi_generic_write() - transmit data using a generic write packet
* @dsi: DSI peripheral device
* @payload: buffer containing the payload
* @size: size of payload buffer
*
* This function will automatically choose the right data type depending on
* the payload length.
*
* Return: The number of bytes transmitted on success or a negative error code
* on failure.
*/
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_buf = payload,
.tx_len = size
};
switch (size) {
case 0:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
break;
default:
msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
break;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
/**
* mipi_dsi_generic_read() - receive data using a generic read packet
* @dsi: DSI peripheral device
* @params: buffer containing the request parameters
* @num_params: number of request parameters
* @data: buffer in which to return the received data
* @size: size of receive buffer
*
* This function will automatically choose the right data type depending on
* the number of parameters passed in.
*
* Return: The number of bytes successfully read or a negative error code on
* failure.
*/
ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
size_t num_params, void *data, size_t size)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_len = num_params,
.tx_buf = params,
.rx_len = size,
.rx_buf = data
};
switch (num_params) {
case 0:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
break;
default:
return -EINVAL;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
/**
* mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
* @dsi: DSI peripheral device
* @data: buffer containing data to be transmitted
* @len: size of transmission buffer
*
* 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_buffer(struct mipi_dsi_device *dsi,
const void *data, size_t len)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.tx_buf = data,
.tx_len = len
};
switch (len) {
case 0:
return -EINVAL;
case 1:
msg.type = MIPI_DSI_DCS_SHORT_WRITE;
break;
case 2:
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
break;
default:
msg.type = MIPI_DSI_DCS_LONG_WRITE;
break;
}
return mipi_dsi_device_transfer(dsi, &msg);
}
/**
* mipi_dsi_dcs_write() - send DCS write command
* @dsi: DSI peripheral device
* @cmd: DCS command
* @data: buffer containing the command payload
* @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)
{
ssize_t err;
size_t size;
u8 *tx;
if (len > 0) {
size = 1 + len;
tx = malloc(size);
if (!tx)
return -ENOMEM;
/* concatenate the DCS command byte and the payload */
tx[0] = cmd;
memcpy(&tx[1], data, len);
} else {
tx = &cmd;
size = 1;
}
err = mipi_dsi_dcs_write_buffer(dsi, tx, size);
if (len > 0)
free(tx);
return err;
}
/**
* mipi_dsi_dcs_read() - send DCS read request command
* @dsi: DSI peripheral device
* @cmd: DCS command
* @data: buffer in which to receive data
* @len: size of receive buffer
* Return: The number of bytes read or a negative error code on failure.
*/
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
size_t len)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_DCS_READ,
.tx_buf = &cmd,
.tx_len = 1,
.rx_buf = data,
.rx_len = len
};
return mipi_dsi_device_transfer(dsi, &msg);
}
/**
* mipi_dsi_dcs_nop() - send DCS nop packet
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_soft_reset() - perform a software reset of the display module
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0);
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_get_power_mode() - query the display module's current power
* mode
* @dsi: DSI peripheral device
* @mode: return location for the current power mode
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
sizeof(*mode));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
/**
* mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image
* data used by the interface
* @dsi: DSI peripheral device
* @format: return location for the pixel format
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format,
sizeof(*format));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
/**
* mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
* display module except interface communication
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
* module
* @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_off() - stop displaying the image data on the
* display device
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_display_on() - start displaying the image data on the
* display device
* @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
* @dsi: DSI peripheral device
* @start: first column of frame memory
* @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
* @dsi: DSI peripheral device
* @start: first page of frame memory
* @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_off() - turn off the display module's Tearing Effect
* output signal on the TE signal line
* @dsi: DSI peripheral device
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
{
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
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.
* @dsi: DSI peripheral device
* @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;
}
/**
* mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
* data used by the interface
* @dsi: DSI peripheral device
* @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_tear_scanline() - set the scanline to use as trigger for
* the Tearing Effect output signal of the display module
* @dsi: DSI peripheral device
* @scanline: scanline to use as trigger
*
* Return: 0 on success or a negative error code on failure
*/
int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline)
{
u8 payload[3] = { MIPI_DCS_SET_TEAR_SCANLINE, scanline >> 8,
scanline & 0xff };
ssize_t err;
err = mipi_dsi_generic_write(dsi, payload, sizeof(payload));
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the
* display
* @dsi: DSI peripheral device
* @brightness: brightness value
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
u16 brightness)
{
u8 payload[2] = { brightness & 0xff, brightness >> 8 };
ssize_t err;
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
payload, sizeof(payload));
if (err < 0)
return err;
return 0;
}
/**
* mipi_dsi_dcs_get_display_brightness() - gets the current brightness value
* of the display
* @dsi: DSI peripheral device
* @brightness: brightness value
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
u16 *brightness)
{
ssize_t err;
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
brightness, sizeof(*brightness));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}

View File

@ -4,6 +4,8 @@
* SPDX-License-Identifier: GPL-2.0+
*/
#include <drm/drm_mipi_dsi.h>
#include <config.h>
#include <common.h>
#include <errno.h>
@ -15,13 +17,15 @@
#include <dm/of_access.h>
#include <syscon.h>
#include <asm/arch-rockchip/clock.h>
#include <linux/iopoll.h>
#include "rockchip_display.h"
#include "rockchip_crtc.h"
#include "rockchip_connector.h"
#include "rockchip_panel.h"
#include "rockchip_phy.h"
#include "rockchip_mipi_dsi.h"
#define UPDATE(v, h, l) (((v) << (l)) & GENMASK((h), (l)))
#define DSI_VERSION 0x00
#define DSI_PWR_UP 0x04
@ -300,7 +304,7 @@ static int genif_wait_w_pld_fifo_not_full(struct dw_mipi_dsi *dsi)
int ret;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
sts, !(sts & GEN_PLD_W_FULL), 10,
sts, !(sts & GEN_PLD_W_FULL),
CMD_PKT_STATUS_TIMEOUT_US);
if (ret < 0) {
printf("generic write payload fifo is full\n");
@ -316,7 +320,7 @@ static int genif_wait_cmd_fifo_not_full(struct dw_mipi_dsi *dsi)
int ret;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
sts, !(sts & GEN_CMD_FULL), 10,
sts, !(sts & GEN_CMD_FULL),
CMD_PKT_STATUS_TIMEOUT_US);
if (ret < 0) {
printf("generic write cmd fifo is full\n");
@ -334,7 +338,7 @@ static int genif_wait_write_fifo_empty(struct dw_mipi_dsi *dsi)
mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
sts, (sts & mask) == mask, 10,
sts, (sts & mask) == mask,
CMD_PKT_STATUS_TIMEOUT_US);
if (ret < 0) {
printf("generic write fifo is full\n");
@ -469,8 +473,7 @@ static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
}
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
val, val & PHY_LOCK,
1000, PHY_STATUS_TIMEOUT_US);
val, val & PHY_LOCK, PHY_STATUS_TIMEOUT_US);
if (ret < 0) {
dev_err(dsi->dev, "PHY is not locked\n");
return ret;
@ -481,7 +484,7 @@ static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
mask = PHY_STOPSTATELANE;
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
val, (val & mask) == mask,
1000, PHY_STATUS_TIMEOUT_US);
PHY_STATUS_TIMEOUT_US);
if (ret < 0) {
dev_err(dsi->dev, "lane module is not in stop state\n");
return ret;
@ -525,7 +528,7 @@ static void dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
testif_write(dsi, 0x44, HSFREQRANGE(hsfreqrange));
txbyteclkhs = dsi->lane_mbps >> 3;
counter = txbyteclkhs * 60 / NSEC_PER_USEC;
counter = txbyteclkhs * 60 / 1000;
testif_write(dsi, 0x60, 0x80 | counter);
testif_write(dsi, 0x70, 0x80 | counter);
@ -549,7 +552,7 @@ static unsigned long dw_mipi_dsi_get_lane_rate(struct dw_mipi_dsi *dsi)
/* optional override of the desired bandwidth */
value = dev_read_u32_default(dsi->dev, "rockchip,lane-rate", 0);
if (value > 0)
return value * USEC_PER_SEC;
return value * 1000 * 1000;
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
if (bpp < 0)
@ -629,7 +632,7 @@ static void dw_mipi_dsi_set_pll(struct dw_mipi_dsi *dsi, unsigned long rate)
}
}
dsi->lane_mbps = best_freq / USEC_PER_SEC;
dsi->lane_mbps = best_freq / 1000 / 1000;
dsi->dphy.input_div = best_prediv;
dsi->dphy.feedback_div = best_fbdiv;
if (dsi->slave) {
@ -648,7 +651,7 @@ static int dw_mipi_dsi_read_from_fifo(struct dw_mipi_dsi *dsi,
int ret;
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_RD_CMD_BUSY), 50, 5000);
val, !(val & GEN_RD_CMD_BUSY), 5000);
if (ret) {
printf("entire response isn't stored in the FIFO\n");
return ret;
@ -657,8 +660,7 @@ static int dw_mipi_dsi_read_from_fifo(struct dw_mipi_dsi *dsi,
/* Receive payload */
for (length = msg->rx_len; length; length -= 4) {
ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_R_EMPTY),
50, 5000);
val, !(val & GEN_PLD_R_EMPTY), 5000);
if (ret) {
printf("Read payload FIFO is empty\n");
return ret;
@ -713,7 +715,7 @@ static ssize_t dw_mipi_dsi_transfer(struct dw_mipi_dsi *dsi,
int ret;
int val;
if (dsi->mode_flags & MIPI_DSI_MODE_LPM) {
if (msg->flags & MIPI_DSI_MSG_USE_LPM) {
dsi_update_bits(dsi, DSI_VID_MODE_CFG, LP_CMD_EN, LP_CMD_EN);
dsi_update_bits(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS, 0);
} else {
@ -858,15 +860,6 @@ static ssize_t dw_mipi_dsi_transfer(struct dw_mipi_dsi *dsi,
return msg->rx_len ? msg->rx_len : msg->tx_len;
}
static ssize_t dw_mipi_dsi_connector_transfer(struct display_state *state,
const struct mipi_dsi_msg *msg)
{
struct connector_state *conn_state = &state->conn_state;
struct dw_mipi_dsi *dsi = dev_get_priv(conn_state->dev);
return dw_mipi_dsi_transfer(dsi, msg);
}
static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
{
u32 val = LP_VACT_EN | LP_VFP_EN | LP_VBP_EN | LP_VSA_EN |
@ -1017,7 +1010,7 @@ static int dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
{
u32 lbcc;
lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
lbcc = hcomponent * dsi->lane_mbps * 1000 / 8;
if (dsi->mode->clock == 0) {
printf("dsi mode clock is 0!\n");
@ -1089,20 +1082,9 @@ static int dw_mipi_dsi_connector_init(struct display_state *state)
{
struct connector_state *conn_state = &state->conn_state;
struct dw_mipi_dsi *dsi = dev_get_priv(conn_state->dev);
struct rockchip_panel *panel = state_get_panel(state);
dsi->dphy.phy = conn_state->phy;
dsi->lanes = dev_read_u32_default(panel->dev, "dsi,lanes", 4);
dsi->format = dev_read_u32_default(panel->dev, "dsi,format",
MIPI_DSI_FMT_RGB888);
dsi->mode_flags = dev_read_u32_default(panel->dev, "dsi,flags",
MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM |
MIPI_DSI_MODE_EOT_PACKET);
dsi->channel = dev_read_u32_default(panel->dev, "reg", 0);
conn_state->output_mode = ROCKCHIP_OUT_MODE_P888;
conn_state->color_space = V4L2_COLORSPACE_DEFAULT;
conn_state->type = DRM_MODE_CONNECTOR_DSI;
@ -1122,7 +1104,7 @@ static int dw_mipi_dsi_connector_init(struct display_state *state)
static void dw_mipi_dsi_set_hs_clk(struct dw_mipi_dsi *dsi, unsigned long rate)
{
rate = rockchip_phy_set_pll(dsi->dphy.phy, rate);
dsi->lane_mbps = rate / USEC_PER_SEC;
dsi->lane_mbps = rate / 1000 / 1000;
}
static void dw_mipi_dsi_host_init(struct dw_mipi_dsi *dsi)
@ -1256,7 +1238,6 @@ static const struct rockchip_connector_funcs dw_mipi_dsi_connector_funcs = {
.unprepare = dw_mipi_dsi_connector_unprepare,
.enable = dw_mipi_dsi_connector_enable,
.disable = dw_mipi_dsi_connector_disable,
.transfer = dw_mipi_dsi_connector_transfer,
};
static int dw_mipi_dsi_dual_channel_probe(struct dw_mipi_dsi *master)
@ -1515,11 +1496,89 @@ static const struct udevice_id dw_mipi_dsi_ids[] = {
{}
};
static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct dw_mipi_dsi *dsi = dev_get_priv(host->dev);
return dw_mipi_dsi_transfer(dsi, msg);
}
static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi *dsi = dev_get_priv(host->dev);
if (device->lanes < 1 || device->lanes > 8)
return -EINVAL;
dsi->lanes = device->lanes;
dsi->channel = device->channel;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
return 0;
}
static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
.attach = dw_mipi_dsi_host_attach,
.transfer = dw_mipi_dsi_host_transfer,
};
static int dw_mipi_dsi_bind(struct udevice *dev)
{
struct mipi_dsi_host *host = dev_get_platdata(dev);
host->dev = dev;
host->ops = &dw_mipi_dsi_host_ops;
return dm_scan_fdt_dev(dev);
}
static int dw_mipi_dsi_child_post_bind(struct udevice *dev)
{
struct mipi_dsi_host *host = dev_get_platdata(dev->parent);
struct mipi_dsi_device *device = dev_get_parent_platdata(dev);
device->dev = dev;
device->host = host;
device->lanes = dev_read_u32_default(dev, "dsi,lanes", 4);
device->format = dev_read_u32_default(dev, "dsi,format",
MIPI_DSI_FMT_RGB888);
device->mode_flags = dev_read_u32_default(dev, "dsi,flags",
MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_VIDEO_HBP |
MIPI_DSI_MODE_LPM |
MIPI_DSI_MODE_EOT_PACKET);
device->channel = dev_read_u32_default(dev, "reg", 0);
return 0;
}
static int dw_mipi_dsi_child_pre_probe(struct udevice *dev)
{
struct mipi_dsi_device *device = dev_get_parent_platdata(dev);
int ret;
ret = mipi_dsi_attach(device);
if (ret) {
dev_err(dev, "mipi_dsi_attach() failed: %d\n", ret);
return ret;
}
return 0;
}
U_BOOT_DRIVER(dw_mipi_dsi) = {
.name = "dw_mipi_dsi",
.id = UCLASS_DISPLAY,
.of_match = dw_mipi_dsi_ids,
.probe = dw_mipi_dsi_probe,
.bind = dm_scan_fdt_dev,
.bind = dw_mipi_dsi_bind,
.priv_auto_alloc_size = sizeof(struct dw_mipi_dsi),
.per_child_platdata_auto_alloc_size = sizeof(struct mipi_dsi_device),
.platdata_auto_alloc_size = sizeof(struct mipi_dsi_host),
.child_post_bind = dw_mipi_dsi_child_post_bind,
.child_pre_probe = dw_mipi_dsi_child_pre_probe,
};

View File

@ -20,11 +20,10 @@
#include "rockchip_display.h"
#include "rockchip_crtc.h"
#include "rockchip_connector.h"
#include "rockchip_phy.h"
#define INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT 1000
/*#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))*/
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
/* REG: 0x00 */
#define PRE_PLL_REFCLK_SEL_MASK BIT(0)

View File

@ -7,8 +7,6 @@
#ifndef _ROCKCHIP_CONNECTOR_H_
#define _ROCKCHIP_CONNECTOR_H_
#include "rockchip_mipi_dsi.h"
struct rockchip_connector {
const struct rockchip_connector_funcs *funcs;
@ -58,9 +56,6 @@ struct rockchip_connector_funcs {
* Save data to dts, then you can share data to kernel space.
*/
int (*fixup_dts)(struct display_state *state, void *blob);
/* transmit a DSI packet */
ssize_t (*transfer)(struct display_state *state,
const struct mipi_dsi_msg *msg);
};
const struct rockchip_connector *

View File

@ -20,7 +20,8 @@
#include "rockchip_connector.h"
#include "rockchip_phy.h"
#include "rockchip_panel.h"
#include "rockchip_lvds.h"
#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK(h, l) << 16))
#define PX30_GRF_PD_VO_CON1 0x0438
#define PX30_LVDS_SELECT(x) HIWORD_UPDATE(x, 14, 13)

View File

@ -1,340 +0,0 @@
/*
* (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <errno.h>
#include <malloc.h>
#include <asm/unaligned.h>
#include <linux/list.h>
#include "rockchip_display.h"
#include "rockchip_crtc.h"
#include "rockchip_connector.h"
#include "rockchip_mipi_dsi.h"
void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
unsigned long period)
{
/* Global Operation Timing Parameters */
timing->clkmiss = 0;
timing->clkpost = 70 + 52 * period;
timing->clkpre = 8 * period;
timing->clkprepare = 65;
timing->clksettle = 95;
timing->clktermen = 0;
timing->clktrail = 80;
timing->clkzero = 260;
timing->dtermen = 0;
timing->eot = 0;
timing->hsexit = 120;
timing->hsprepare = 65 + 4 * period;
timing->hszero = 145 + 6 * period;
timing->hssettle = 85 + 6 * period;
timing->hsskip = 40;
timing->hstrail = max(8 * period, 60 + 4 * period);
timing->init = 100000;
timing->lpx = 60;
timing->taget = 5 * timing->lpx;
timing->tago = 4 * timing->lpx;
timing->tasure = 2 * timing->lpx;
timing->wakeup = 1000000;
}
ssize_t mipi_dsi_generic_write(struct display_state *state,
const void *payload, size_t size)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
struct mipi_dsi_msg msg;
if (!connector || !connector->funcs || !connector->funcs->transfer) {
printf("%s: failed to find connector transfer funcs\n", __func__);
return -ENODEV;
}
memset(&msg, 0, sizeof(msg));
msg.channel = 0;
msg.tx_buf = payload;
msg.tx_len = size;
msg.flags |= MIPI_DSI_MSG_USE_LPM;
switch (size) {
case 0:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
break;
default:
msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
break;
}
return connector->funcs->transfer(state, &msg);
}
ssize_t mipi_dsi_dcs_write(struct display_state *state,
const void *payload, size_t size)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
struct mipi_dsi_msg msg;
if (!connector || !connector->funcs || !connector->funcs->transfer) {
printf("%s: failed to find connector transfer funcs\n", __func__);
return -ENODEV;
}
memset(&msg, 0, sizeof(msg));
msg.channel = 0;
msg.tx_buf = payload;
msg.tx_len = size;
msg.flags |= MIPI_DSI_MSG_USE_LPM;
switch (size) {
case 0:
return -EINVAL;
case 1:
msg.type = MIPI_DSI_DCS_SHORT_WRITE;
break;
case 2:
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
break;
default:
msg.type = MIPI_DSI_DCS_LONG_WRITE;
break;
}
return connector->funcs->transfer(state, &msg);
}
ssize_t mipi_dsi_generic_read(struct display_state *state, const void *params,
size_t num_params, void *data, size_t size)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
struct mipi_dsi_msg msg;
if (!connector || !connector->funcs || !connector->funcs->transfer) {
printf("%s: failed to find connector transfer funcs\n", __func__);
return -ENODEV;
}
memset(&msg, 0, sizeof(msg));
msg.channel = 0;
msg.tx_buf = params;
msg.tx_len = num_params;
msg.rx_len = size;
msg.rx_buf = data;
msg.flags |= MIPI_DSI_MSG_USE_LPM;
switch (num_params) {
case 0:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
break;
case 1:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
break;
case 2:
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
break;
default:
return -EINVAL;
}
return connector->funcs->transfer(state, &msg);
}
ssize_t mipi_dsi_dcs_read(struct display_state *state, u8 cmd, void *data,
size_t len)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
struct mipi_dsi_msg msg;
if (!connector || !connector->funcs || !connector->funcs->transfer) {
printf("%s: failed to find connector transfer funcs\n", __func__);
return -ENODEV;
}
memset(&msg, 0, sizeof(msg));
msg.channel = 0;
msg.type = MIPI_DSI_DCS_READ;
msg.tx_buf = &cmd;
msg.tx_len = 1;
msg.rx_len = len;
msg.rx_buf = data;
msg.flags |= MIPI_DSI_MSG_USE_LPM;
return connector->funcs->transfer(state, &msg);
}
ssize_t mipi_dsi_shutdown_peripheral(struct display_state *state)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
struct mipi_dsi_msg msg = {
.channel = 0,
.type = MIPI_DSI_SHUTDOWN_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
.flags = MIPI_DSI_MSG_USE_LPM,
};
if (!connector)
return -ENODEV;
if (!connector->funcs || !connector->funcs->transfer)
return -ENOSYS;
return connector->funcs->transfer(state, &msg);
}
ssize_t mipi_dsi_turn_on_peripheral(struct display_state *state)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
struct mipi_dsi_msg msg = {
.channel = 0,
.type = MIPI_DSI_TURN_ON_PERIPHERAL,
.tx_buf = (u8 [2]) { 0, 0 },
.tx_len = 2,
.flags = MIPI_DSI_MSG_USE_LPM,
};
if (!connector)
return -ENODEV;
if (!connector->funcs || !connector->funcs->transfer)
return -ENOSYS;
return connector->funcs->transfer(state, &msg);
}
int mipi_dsi_dcs_get_power_mode(struct display_state *state, u8 *mode)
{
ssize_t err;
err = mipi_dsi_dcs_read(state, MIPI_DCS_GET_POWER_MODE, mode,
sizeof(*mode));
if (err <= 0) {
if (err == 0)
err = -ENODATA;
return err;
}
return 0;
}
int mipi_dsi_set_maximum_return_packet_size(struct display_state *state,
u16 value)
{
struct connector_state *conn_state = &state->conn_state;
const struct rockchip_connector *connector = conn_state->connector;
u8 tx[2] = { value & 0xff, value >> 8 };
struct mipi_dsi_msg msg = {
.channel = 0,
.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
.tx_len = sizeof(tx),
.tx_buf = tx,
};
if (!connector)
return -ENODEV;
if (!connector->funcs || !connector->funcs->transfer)
return -ENOSYS;
return connector->funcs->transfer(state, &msg);
}
static bool mipi_dsi_packet_format_is_short(u8 type)
{
switch (type) {
case MIPI_DSI_V_SYNC_START:
case MIPI_DSI_V_SYNC_END:
case MIPI_DSI_H_SYNC_START:
case MIPI_DSI_H_SYNC_END:
case MIPI_DSI_END_OF_TRANSMISSION:
case MIPI_DSI_COLOR_MODE_OFF:
case MIPI_DSI_COLOR_MODE_ON:
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
case MIPI_DSI_TURN_ON_PERIPHERAL:
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_READ:
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
return true;
}
return false;
}
static bool mipi_dsi_packet_format_is_long(u8 type)
{
switch (type) {
case MIPI_DSI_NULL_PACKET:
case MIPI_DSI_BLANKING_PACKET:
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
case MIPI_DSI_PACKED_PIXEL_STREAM_30:
case MIPI_DSI_PACKED_PIXEL_STREAM_36:
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
return true;
}
return false;
}
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg)
{
if (!packet || !msg)
return -EINVAL;
/* do some minimum sanity checking */
if (!mipi_dsi_packet_format_is_short(msg->type) &&
!mipi_dsi_packet_format_is_long(msg->type))
return -EINVAL;
if (msg->channel > 3)
return -EINVAL;
memset(packet, 0, sizeof(*packet));
packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
if (mipi_dsi_packet_format_is_long(msg->type)) {
packet->header[1] = (msg->tx_len >> 0) & 0xff;
packet->header[2] = (msg->tx_len >> 8) & 0xff;
packet->payload_length = msg->tx_len;
packet->payload = msg->tx_buf;
} else {
const u8 *tx = msg->tx_buf;
packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
}
packet->size = sizeof(packet->header) + packet->payload_length;
return 0;
}

View File

@ -1,202 +0,0 @@
/*
* (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __ROCKCHIP_MIPI_DSI_H__
#define __ROCKCHIP_MIPI_DSI_H__
#include <mipi_display.h>
#define MSEC_PER_SEC 1000L
#define USEC_PER_MSEC 1000L
#define NSEC_PER_USEC 1000L
#define NSEC_PER_MSEC 1000000L
#define USEC_PER_SEC 1000000L
#define NSEC_PER_SEC 1000000000L
#define FSEC_PER_SEC 1000000000000000LL
#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
#define UPDATE(v, h, l) (((v) << (l)) & GENMASK((h), (l)))
#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK(h, l) << 16))
#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
({ \
int try = 100; \
for (;;) { \
(val) = readl(addr); \
if (cond) \
break; \
try--; \
if (!try) \
break; \
if (sleep_us) \
udelay(sleep_us >> 2); \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
/* request ACK from peripheral */
#define MIPI_DSI_MSG_REQ_ACK BIT(0)
/* use Low Power Mode to transmit message */
#define MIPI_DSI_MSG_USE_LPM BIT(1)
/**
* struct mipi_dsi_msg - read/write DSI buffer
* @channel: virtual channel id
* @type: payload data type
* @flags: flags controlling this message transmission
* @tx_len: length of @tx_buf
* @tx_buf: data to be written
* @rx_len: length of @rx_buf
* @rx_buf: data to be read, or NULL
*/
struct mipi_dsi_msg {
u8 channel;
u8 type;
u16 flags;
size_t tx_len;
const void *tx_buf;
size_t rx_len;
void *rx_buf;
};
/**
* struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format
* @size: size (in bytes) of the packet
* @header: the four bytes that make up the header (Data ID, Word Count or
* Packet Data, and ECC)
* @payload_length: number of bytes in the payload
* @payload: a pointer to a buffer containing the payload, if any
*/
struct mipi_dsi_packet {
size_t size;
u8 header[4];
size_t payload_length;
const u8 *payload;
};
/* DSI mode flags */
/* video mode */
#define MIPI_DSI_MODE_VIDEO BIT(0)
/* video burst mode */
#define MIPI_DSI_MODE_VIDEO_BURST BIT(1)
/* video pulse mode */
#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2)
/* enable auto vertical count mode */
#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3)
/* enable hsync-end packets in vsync-pulse and v-porch area */
#define MIPI_DSI_MODE_VIDEO_HSE BIT(4)
/* disable hfront-porch area */
#define MIPI_DSI_MODE_VIDEO_HFP BIT(5)
/* disable hback-porch area */
#define MIPI_DSI_MODE_VIDEO_HBP BIT(6)
/* disable hsync-active area */
#define MIPI_DSI_MODE_VIDEO_HSA BIT(7)
/* flush display FIFO on vsync pulse */
#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8)
/* disable EoT packets in HS mode */
#define MIPI_DSI_MODE_EOT_PACKET BIT(9)
/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10)
/* transmit data in low power */
#define MIPI_DSI_MODE_LPM BIT(11)
enum mipi_dsi_pixel_format {
MIPI_DSI_FMT_RGB888,
MIPI_DSI_FMT_RGB666,
MIPI_DSI_FMT_RGB666_PACKED,
MIPI_DSI_FMT_RGB565,
};
/**
* mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any
* given pixel format defined by the MIPI DSI
* specification
* @fmt: MIPI DSI pixel format
*
* Returns: The number of bits per pixel of the given pixel format.
*/
static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
{
switch (fmt) {
case MIPI_DSI_FMT_RGB888:
case MIPI_DSI_FMT_RGB666:
return 24;
case MIPI_DSI_FMT_RGB666_PACKED:
return 18;
case MIPI_DSI_FMT_RGB565:
return 16;
}
return -EINVAL;
}
/**
* enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
* @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
* information only
* @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
* V-Blanking and H-Blanking information
*/
enum mipi_dsi_dcs_tear_mode {
MIPI_DSI_DCS_TEAR_MODE_VBLANK,
MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
};
#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2)
#define MIPI_DSI_DCS_POWER_MODE_NORMAL (1 << 3)
#define MIPI_DSI_DCS_POWER_MODE_SLEEP (1 << 4)
#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5)
#define MIPI_DSI_DCS_POWER_MODE_IDLE (1 << 6)
struct mipi_dphy_timing {
unsigned int clkmiss;
unsigned int clkpost;
unsigned int clkpre;
unsigned int clkprepare;
unsigned int clksettle;
unsigned int clktermen;
unsigned int clktrail;
unsigned int clkzero;
unsigned int dtermen;
unsigned int eot;
unsigned int hsexit;
unsigned int hsprepare;
unsigned int hszero;
unsigned int hssettle;
unsigned int hsskip;
unsigned int hstrail;
unsigned int init;
unsigned int lpx;
unsigned int taget;
unsigned int tago;
unsigned int tasure;
unsigned int wakeup;
};
void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
unsigned long period);
ssize_t mipi_dsi_dcs_write(struct display_state *state,
const void *payload, size_t size);
ssize_t mipi_dsi_generic_write(struct display_state *state,
const void *payload, size_t size);
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg);
ssize_t mipi_dsi_dcs_read(struct display_state *state, u8 cmd, void *data,
size_t len);
ssize_t mipi_dsi_generic_read(struct display_state *state, const void *params,
size_t num_params, void *data, size_t size);
ssize_t mipi_dsi_shutdown_peripheral(struct display_state *state);
ssize_t mipi_dsi_turn_on_peripheral(struct display_state *state);
int mipi_dsi_set_maximum_return_packet_size(struct display_state *state,
u16 value);
int mipi_dsi_dcs_get_pixel_format(struct display_state *state, u8 *format);
#endif /* __ROCKCHIP_MIPI_DSI__ */

View File

@ -4,6 +4,8 @@
* SPDX-License-Identifier: GPL-2.0+
*/
#include <drm/drm_mipi_dsi.h>
#include <config.h>
#include <common.h>
#include <errno.h>
@ -21,7 +23,6 @@
#include "rockchip_display.h"
#include "rockchip_crtc.h"
#include "rockchip_connector.h"
#include "rockchip_mipi_dsi.h"
#include "rockchip_panel.h"
struct rockchip_cmd_header {
@ -209,7 +210,7 @@ static int rockchip_panel_send_spi_cmds(struct display_state *state,
return 0;
}
static int rockchip_panel_send_dsi_cmds(struct display_state *state,
static int rockchip_panel_send_dsi_cmds(struct mipi_dsi_device *dsi,
struct rockchip_panel_cmds *cmds)
{
int i, ret;
@ -219,24 +220,25 @@ static int rockchip_panel_send_dsi_cmds(struct display_state *state,
for (i = 0; i < cmds->cmd_cnt; i++) {
struct rockchip_cmd_desc *desc = &cmds->cmds[i];
const struct rockchip_cmd_header *header = &desc->header;
switch (desc->header.data_type) {
switch (header->data_type) {
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
case MIPI_DSI_GENERIC_LONG_WRITE:
ret = mipi_dsi_generic_write(state, desc->payload,
desc->header.payload_length);
ret = mipi_dsi_generic_write(dsi, desc->payload,
header->payload_length);
break;
case MIPI_DSI_DCS_SHORT_WRITE:
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
case MIPI_DSI_DCS_LONG_WRITE:
ret = mipi_dsi_dcs_write(state, desc->payload,
desc->header.payload_length);
ret = mipi_dsi_dcs_write_buffer(dsi, desc->payload,
header->payload_length);
break;
default:
printf("unsupport command data type: %d\n",
desc->header.data_type);
header->data_type);
return -EINVAL;
}
@ -245,8 +247,8 @@ static int rockchip_panel_send_dsi_cmds(struct display_state *state,
return ret;
}
if (desc->header.delay_ms)
mdelay(desc->header.delay_ms);
if (header->delay_ms)
mdelay(header->delay_ms);
}
return 0;
@ -256,6 +258,7 @@ static void panel_simple_prepare(struct rockchip_panel *panel)
{
struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
struct mipi_dsi_device *dsi = dev_get_parent_platdata(panel->dev);
int ret;
if (priv->prepared)
@ -290,8 +293,7 @@ static void panel_simple_prepare(struct rockchip_panel *panel)
ret = rockchip_panel_send_mcu_cmds(panel->state,
plat->on_cmds);
else
ret = rockchip_panel_send_dsi_cmds(panel->state,
plat->on_cmds);
ret = rockchip_panel_send_dsi_cmds(dsi, plat->on_cmds);
if (ret)
printf("failed to send on cmds: %d\n", ret);
}
@ -303,6 +305,7 @@ static void panel_simple_unprepare(struct rockchip_panel *panel)
{
struct rockchip_panel_plat *plat = dev_get_platdata(panel->dev);
struct rockchip_panel_priv *priv = dev_get_priv(panel->dev);
struct mipi_dsi_device *dsi = dev_get_parent_platdata(panel->dev);
int ret;
if (!priv->prepared)
@ -316,8 +319,7 @@ static void panel_simple_unprepare(struct rockchip_panel *panel)
ret = rockchip_panel_send_mcu_cmds(panel->state,
plat->off_cmds);
else
ret = rockchip_panel_send_dsi_cmds(panel->state,
plat->off_cmds);
ret = rockchip_panel_send_dsi_cmds(dsi, plat->off_cmds);
if (ret)
printf("failed to send off cmds: %d\n", ret);
}

View File

@ -17,6 +17,8 @@
#include "rockchip_connector.h"
#include "rockchip_phy.h"
#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK(h, l) << 16))
#define PX30_GRF_PD_VO_CON1 0x0438
#define PX30_RGB_DATA_SYNC_BYPASS(v) HIWORD_UPDATE(v, 3, 3)
#define PX30_RGB_VOP_SEL(v) HIWORD_UPDATE(v, 2, 2)

252
include/drm/drm_mipi_dsi.h Normal file
View File

@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* MIPI DSI Bus
*
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
* Andrzej Hajda <a.hajda@samsung.com>
*/
#ifndef __DRM_MIPI_DSI_H__
#define __DRM_MIPI_DSI_H__
#include <mipi_display.h>
#include <dm/device.h>
struct mipi_dsi_host;
struct mipi_dsi_device;
/* request ACK from peripheral */
#define MIPI_DSI_MSG_REQ_ACK BIT(0)
/* use Low Power Mode to transmit message */
#define MIPI_DSI_MSG_USE_LPM BIT(1)
/**
* struct mipi_dsi_msg - read/write DSI buffer
* @channel: virtual channel id
* @type: payload data type
* @flags: flags controlling this message transmission
* @tx_len: length of @tx_buf
* @tx_buf: data to be written
* @rx_len: length of @rx_buf
* @rx_buf: data to be read, or NULL
*/
struct mipi_dsi_msg {
u8 channel;
u8 type;
u16 flags;
size_t tx_len;
const void *tx_buf;
size_t rx_len;
void *rx_buf;
};
bool mipi_dsi_packet_format_is_short(u8 type);
bool mipi_dsi_packet_format_is_long(u8 type);
/**
* struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format
* @size: size (in bytes) of the packet
* @header: the four bytes that make up the header (Data ID, Word Count or
* Packet Data, and ECC)
* @payload_length: number of bytes in the payload
* @payload: a pointer to a buffer containing the payload, if any
*/
struct mipi_dsi_packet {
size_t size;
u8 header[4];
size_t payload_length;
const u8 *payload;
};
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
const struct mipi_dsi_msg *msg);
/**
* struct mipi_dsi_host_ops - DSI bus operations
* @attach: attach DSI device to DSI host
* @detach: detach DSI device from DSI host
* @transfer: transmit a DSI packet
*
* DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
* structures. This structure contains information about the type of packet
* being transmitted as well as the transmit and receive buffers. When an
* error is encountered during transmission, this function will return a
* negative error code. On success it shall return the number of bytes
* transmitted for write packets or the number of bytes received for read
* packets.
*
* Note that typically DSI packet transmission is atomic, so the .transfer()
* function will seldomly return anything other than the number of bytes
* contained in the transmit buffer on success.
*/
struct mipi_dsi_host_ops {
int (*attach)(struct mipi_dsi_host *host,
struct mipi_dsi_device *dsi);
int (*detach)(struct mipi_dsi_host *host,
struct mipi_dsi_device *dsi);
ssize_t (*transfer)(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg);
};
/**
* struct mipi_dsi_host - DSI host device
* @dev: driver model device node for this DSI host
* @ops: DSI host operations
* @list: list management
*/
struct mipi_dsi_host {
struct udevice *dev;
const struct mipi_dsi_host_ops *ops;
};
/* DSI mode flags */
/* video mode */
#define MIPI_DSI_MODE_VIDEO BIT(0)
/* video burst mode */
#define MIPI_DSI_MODE_VIDEO_BURST BIT(1)
/* video pulse mode */
#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2)
/* enable auto vertical count mode */
#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3)
/* enable hsync-end packets in vsync-pulse and v-porch area */
#define MIPI_DSI_MODE_VIDEO_HSE BIT(4)
/* disable hfront-porch area */
#define MIPI_DSI_MODE_VIDEO_HFP BIT(5)
/* disable hback-porch area */
#define MIPI_DSI_MODE_VIDEO_HBP BIT(6)
/* disable hsync-active area */
#define MIPI_DSI_MODE_VIDEO_HSA BIT(7)
/* flush display FIFO on vsync pulse */
#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8)
/* disable EoT packets in HS mode */
#define MIPI_DSI_MODE_EOT_PACKET BIT(9)
/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10)
/* transmit data in low power */
#define MIPI_DSI_MODE_LPM BIT(11)
enum mipi_dsi_pixel_format {
MIPI_DSI_FMT_RGB888,
MIPI_DSI_FMT_RGB666,
MIPI_DSI_FMT_RGB666_PACKED,
MIPI_DSI_FMT_RGB565,
};
#define DSI_DEV_NAME_SIZE 20
/**
* struct mipi_dsi_device - DSI peripheral device
* @host: DSI host for this peripheral
* @dev: driver model device node for this peripheral
* @name: DSI peripheral chip type
* @channel: virtual channel assigned to the peripheral
* @format: pixel format for video mode
* @lanes: number of active data lanes
* @mode_flags: DSI operation mode related flags
* @hs_rate: maximum lane frequency for high speed mode in hertz, this should
* be set to the real limits of the hardware, zero is only accepted for
* legacy drivers
* @lp_rate: maximum lane frequency for low power mode in hertz, this should
* be set to the real limits of the hardware, zero is only accepted for
* legacy drivers
*/
struct mipi_dsi_device {
struct mipi_dsi_host *host;
struct udevice *dev;
char name[DSI_DEV_NAME_SIZE];
unsigned int channel;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
unsigned long hs_rate;
unsigned long lp_rate;
};
/**
* mipi_dsi_pixel_format_to_bpp - obtain the number of bits per pixel for any
* given pixel format defined by the MIPI DSI
* specification
* @fmt: MIPI DSI pixel format
*
* Returns: The number of bits per pixel of the given pixel format.
*/
static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
{
switch (fmt) {
case MIPI_DSI_FMT_RGB888:
case MIPI_DSI_FMT_RGB666:
return 24;
case MIPI_DSI_FMT_RGB666_PACKED:
return 18;
case MIPI_DSI_FMT_RGB565:
return 16;
}
return -EINVAL;
}
int mipi_dsi_attach(struct mipi_dsi_device *dsi);
int mipi_dsi_detach(struct mipi_dsi_device *dsi);
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value);
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size);
ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
size_t num_params, void *data, size_t size);
/**
* enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
* @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
* information only
* @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
* V-Blanking and H-Blanking information
*/
enum mipi_dsi_dcs_tear_mode {
MIPI_DSI_DCS_TEAR_MODE_VBLANK,
MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
};
#define MIPI_DSI_DCS_POWER_MODE_DISPLAY BIT(2)
#define MIPI_DSI_DCS_POWER_MODE_NORMAL BIT(3)
#define MIPI_DSI_DCS_POWER_MODE_SLEEP BIT(4)
#define MIPI_DSI_DCS_POWER_MODE_PARTIAL BIT(5)
#define MIPI_DSI_DCS_POWER_MODE_IDLE BIT(6)
ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
const void *data, size_t len);
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
const void *data, size_t len);
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
size_t len);
int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode);
int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format);
int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
u16 end);
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
u16 end);
int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
enum mipi_dsi_dcs_tear_mode mode);
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline);
int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
u16 brightness);
int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
u16 *brightness);
#endif /* __DRM_MIPI_DSI__ */