blob: d6dc29e43ba2fc5c8ecc7c897387262f9b430aca [file] [log] [blame]
/*
* (C) Copyright 2012
* Marvell Semiconductor <www.marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <malloc.h>
#include <mmp_disp.h>
#include <linux/types.h>
#include <asm/io.h>
#include "tc35876x.h"
#define dsi_ex_pixel_cnt 0
#define dsi_hex_en 0
/* (Unit: Mhz) */
#define dsi_lpclk 3
#define to_dsi_bcnt(timing, bpp) (((timing) * (bpp)) >> 3)
static unsigned int dsi_lane[5] = {0, 0x1, 0x3, 0x7, 0xf};
/* dsi phy timing */
static struct dsi_phy_timing phy = {
.hs_prep_constant = 40, /* Unit: ns. */
.hs_prep_ui = 4,
.hs_zero_constant = 145,
.hs_zero_ui = 10,
.hs_trail_constant = 60,
.hs_trail_ui = 4,
.hs_exit_constant = 100,
.hs_exit_ui = 0,
.ck_zero_constant = 300,
.ck_zero_ui = 0,
.ck_trail_constant = 60,
.ck_trail_ui = 0,
.req_ready = 0x3c,
.wakeup_constant = 1000000,
.wakeup_ui = 0,
/*
* According to the D-PHY spec, Tlpx >= 50 ns
* For the panel otm1281a, it works unstable when Tlpx = 50,
* so increase to 60ns.
*/
.lpx_constant = 60,
.lpx_ui = 0,
};
static unsigned char dsi_bit(unsigned int index, unsigned char *pdata)
{
unsigned char ret;
unsigned int cindex, bindex;
cindex = index / 8;
bindex = index % 8;
if (pdata[cindex] & (0x1 << bindex))
ret = (unsigned char) 0x1;
else
ret = (unsigned char) 0x0;
return ret;
}
static unsigned char calculate_ecc(unsigned char *pdata)
{
unsigned char ret;
unsigned char p[8];
p[7] = (unsigned char) 0x0;
p[6] = (unsigned char) 0x0;
p[5] = (unsigned char) (
(
dsi_bit(10, pdata) ^
dsi_bit(11, pdata) ^
dsi_bit(12, pdata) ^
dsi_bit(13, pdata) ^
dsi_bit(14, pdata) ^
dsi_bit(15, pdata) ^
dsi_bit(16, pdata) ^
dsi_bit(17, pdata) ^
dsi_bit(18, pdata) ^
dsi_bit(19, pdata) ^
dsi_bit(21, pdata) ^
dsi_bit(22, pdata) ^
dsi_bit(23, pdata)
)
);
p[4] = (unsigned char) (
dsi_bit(4, pdata) ^
dsi_bit(5, pdata) ^
dsi_bit(6, pdata) ^
dsi_bit(7, pdata) ^
dsi_bit(8, pdata) ^
dsi_bit(9, pdata) ^
dsi_bit(16, pdata) ^
dsi_bit(17, pdata) ^
dsi_bit(18, pdata) ^
dsi_bit(19, pdata) ^
dsi_bit(20, pdata) ^
dsi_bit(22, pdata) ^
dsi_bit(23, pdata)
);
p[3] = (unsigned char) (
(
dsi_bit(1, pdata) ^
dsi_bit(2, pdata) ^
dsi_bit(3, pdata) ^
dsi_bit(7, pdata) ^
dsi_bit(8, pdata) ^
dsi_bit(9, pdata) ^
dsi_bit(13, pdata) ^
dsi_bit(14, pdata) ^
dsi_bit(15, pdata) ^
dsi_bit(19, pdata) ^
dsi_bit(20, pdata) ^
dsi_bit(21, pdata) ^
dsi_bit(23, pdata)
)
);
p[2] = (unsigned char) (
(
dsi_bit(0, pdata) ^
dsi_bit(2, pdata) ^
dsi_bit(3, pdata) ^
dsi_bit(5, pdata) ^
dsi_bit(6, pdata) ^
dsi_bit(9, pdata) ^
dsi_bit(11, pdata) ^
dsi_bit(12, pdata) ^
dsi_bit(15, pdata) ^
dsi_bit(18, pdata) ^
dsi_bit(20, pdata) ^
dsi_bit(21, pdata) ^
dsi_bit(22, pdata)
)
);
p[1] = (unsigned char) (
(
dsi_bit(0, pdata) ^
dsi_bit(1, pdata) ^
dsi_bit(3, pdata) ^
dsi_bit(4, pdata) ^
dsi_bit(6, pdata) ^
dsi_bit(8, pdata) ^
dsi_bit(10, pdata) ^
dsi_bit(12, pdata) ^
dsi_bit(14, pdata) ^
dsi_bit(17, pdata) ^
dsi_bit(20, pdata) ^
dsi_bit(21, pdata) ^
dsi_bit(22, pdata) ^
dsi_bit(23, pdata)
)
);
p[0] = (unsigned char) (
(
dsi_bit(0, pdata) ^
dsi_bit(1, pdata) ^
dsi_bit(2, pdata) ^
dsi_bit(4, pdata) ^
dsi_bit(5, pdata) ^
dsi_bit(7, pdata) ^
dsi_bit(10, pdata) ^
dsi_bit(11, pdata) ^
dsi_bit(13, pdata) ^
dsi_bit(16, pdata) ^
dsi_bit(20, pdata) ^
dsi_bit(21, pdata) ^
dsi_bit(22, pdata) ^
dsi_bit(23, pdata)
)
);
ret = (unsigned char)(
p[0] |
(p[1] << 0x1) |
(p[2] << 0x2) |
(p[3] << 0x3) |
(p[4] << 0x4) |
(p[5] << 0x5)
);
return ret;
}
static void dsi_lanes_enable(struct dsi_info *di, int en,
unsigned version)
{
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
void *phy_ctrl2_reg = (!DISP_GEN4(version)) ?
(&dsi->phy_ctrl2) : (&dsi->phy.phy_ctrl2);
u32 reg = readl(phy_ctrl2_reg) & ~(0xf << 4);
reg &= ~(0xf << 4);
if (en)
reg |= (dsi_lane[di->lanes] << 4);
writel(reg, phy_ctrl2_reg);
}
static void dsi_send_cmds(struct mmp_disp_info *fbi, u8 *parameter,
u8 count, u8 lp)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
u32 send_data = 0, waddr, timeout, tmp, i, turnaround;
if (lp)
set_dsi_low_power_mode(fbi);
else
dsi_lanes_enable(di, 1, fbi->mi->version);
/* write all packet bytes to packet data buffer */
for (i = 0; i < count; i++) {
send_data |= parameter[i] << ((i % 4) * 8);
if (!((i + 1) % 4)) {
writel(send_data, &dsi->dat0);
waddr = DSI_CFG_CPU_DAT_REQ_MASK |
DSI_CFG_CPU_DAT_RW_MASK |
((i - 3) << DSI_CFG_CPU_DAT_ADDR_SHIFT);
writel(waddr, &dsi->cmd3);
/* wait write operation done */
timeout = 1000;
do {
timeout--;
tmp = readl(&dsi->cmd3);
} while ((tmp & DSI_CFG_CPU_DAT_REQ_MASK) && timeout);
if (!timeout)
printf("DSI write data to the packet data buffer not done.\n");
send_data = 0;
}
}
/* handle last none 4Byte align data */
if (i % 4) {
writel(send_data, &dsi->dat0);
waddr = DSI_CFG_CPU_DAT_REQ_MASK |
DSI_CFG_CPU_DAT_RW_MASK |
((4 * (i / 4)) << DSI_CFG_CPU_DAT_ADDR_SHIFT);
writel(waddr, &dsi->cmd3);
/* wait write operation done */
timeout = 1000;
do {
timeout--;
tmp = readl(&dsi->cmd3);
} while ((tmp & DSI_CFG_CPU_DAT_REQ_MASK) && timeout);
if (!timeout)
printf("DSI write data to the packet data buffer not done.\n");
send_data = 0;
}
if (parameter[0] == DSI_DI_DCS_READ ||
parameter[0] == DSI_DI_SET_MAX_PKT_SIZE)
turnaround = 0x1;
else
turnaround = 0x0;
waddr = DSI_CFG_CPU_CMD_REQ_MASK |
((count == 4) ? DSI_CFG_CPU_SP_MASK : 0) |
(turnaround << DSI_CFG_CPU_TURN_SHIFT) |
(lp ? DSI_CFG_CPU_TXLP_MASK : 0) |
((lp ? count : count - 2) << DSI_CFG_CPU_WC_SHIFT);
/* send out the packet */
writel(waddr, &dsi->cmd0);
/* wait packet be sent out */
timeout = 1000;
do {
timeout--;
tmp = readl(&dsi->cmd0);
} while ((tmp & DSI_CFG_CPU_CMD_REQ_MASK) && timeout);
if (!timeout)
printf("DSI send out packet maybe failed.\n");
}
static unsigned short gs_crc16_generation_code = 0x8408;
static unsigned short calculate_crc16(unsigned char *pdata, unsigned
short count)
{
unsigned short byte_counter;
unsigned char bit_counter;
unsigned char data;
unsigned short crc16_result = 0xFFFF;
if (count > 0) {
for (byte_counter = 0; byte_counter < count;
byte_counter++) {
data = *(pdata + byte_counter);
for (bit_counter = 0; bit_counter < 8; bit_counter++) {
if (((crc16_result & 0x0001) ^ ((0x0001 *
data) & 0x0001)) > 0)
crc16_result = ((crc16_result >> 1)
& 0x7FFF) ^ gs_crc16_generation_code;
else
crc16_result = (crc16_result >> 1)
& 0x7FFF;
data = (data >> 1) & 0x7F;
}
}
}
return crc16_result;
}
void dsi_cmd_array_tx(struct mmp_disp_info *fbi, struct dsi_cmd_desc cmds[],
int count)
{
struct dsi_cmd_desc cmd_line;
u8 command, parameter[DSI_DI_SET_MAX_PKT_SIZE], len;
u32 crc, loop;
for (loop = 0; loop < count; loop++) {
cmd_line = cmds[loop];
command = cmd_line.data_type;
len = cmd_line.length;
memset(parameter, 0x00, len + 6);
parameter[0] = command & 0xff;
switch (command) {
case DSI_DI_DCS_SWRITE:
case DSI_DI_DCS_SWRITE1:
case DSI_DI_DCS_READ:
case DSI_DI_SET_MAX_PKT_SIZE:
memcpy(&parameter[1], cmd_line.data, len);
len = 4;
break;
case DSI_DI_GENERIC_LWRITE:
case DSI_DI_DCS_LWRITE:
parameter[1] = len & 0xff;
parameter[2] = 0;
memcpy(&parameter[4], cmd_line.data, len);
crc = calculate_crc16(&parameter[4], len);
parameter[len + 4] = crc & 0xff;
parameter[len + 5] = (crc >> 8) & 0xff;
len += 6;
break;
default:
printf("%s: data type not supported 0x%8x\n",
__func__, command);
break;
}
parameter[3] = calculate_ecc(parameter);
/* send dsi commands */
dsi_send_cmds(fbi, parameter, len, cmd_line.lp);
if (cmd_line.delay)
udelay(cmd_line.delay * 1000);
}
}
void dsi_cmd_array_rx(struct mmp_disp_info *fbi, struct dsi_buf *dbuf,
struct dsi_cmd_desc cmds[], int count)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
u8 parameter[DSI_DI_SET_MAX_PKT_SIZE];
u32 i, rx_reg, timeout, tmp, packet,
data_pointer, byte_count;
memset(dbuf, 0x0, sizeof(struct dsi_buf));
dsi_cmd_array_tx(fbi, cmds, count);
timeout = 1000;
do {
timeout--;
tmp = readl(&dsi->irq_status);
} while (((tmp & 0x4) == 0) && timeout);
if (!timeout) {
printf("error: dsi didn't receive packet, irq status 0x%x\n",
readl(&dsi->irq_status));
return;
}
if (tmp & IRQ_RX_TRG2)
printf("ACK received\n");
if (tmp & IRQ_RX_TRG1)
printf("TE trigger received\n");
if (tmp & IRQ_RX_ERR) {
printf("error: ACK with error report\n");
tmp = readl(&dsi->rx0_header);
}
packet = readl(&dsi->rx0_status);
data_pointer = (packet & RX_PKT0_PKT_PTR_MASK) >> RX_PKT0_PKT_PTR_SHIFT;
tmp = readl(&dsi->rx_ctrl1);
byte_count = tmp & RX_PKT_BCNT_MASK;
memset(parameter, 0x00, byte_count);
for (i = data_pointer; i < data_pointer + byte_count; i++) {
rx_reg = readl(&dsi->rx_ctrl);
rx_reg &= ~RX_PKT_RD_PTR_MASK;
rx_reg |= RX_PKT_RD_REQ | (i << RX_PKT_RD_PTR_SHIFT);
writel(rx_reg, &dsi->rx_ctrl);
count = 10000;
do {
count--;
rx_reg = readl(&dsi->rx_ctrl);
} while (rx_reg & RX_PKT_RD_REQ && count);
if (!count)
printf("error: Rx packet FIFO error\n");
parameter[i - data_pointer] = rx_reg & 0xff;
}
switch (parameter[0]) {
case DSI_DI_ACK_ERR_RESP:
/* printf("error: Acknowledge with error report\n"); */
break;
case DSI_DI_EOTP:
printf("error: End of Transmission packet\n");
break;
case DSI_DI_GEN_READ1_RESP:
case DSI_DI_DCS_READ1_RESP:
dbuf->data_type = parameter[0];
dbuf->length = 1;
memcpy(dbuf->data, &parameter[1], dbuf->length);
break;
case DSI_DI_GEN_READ2_RESP:
case DSI_DI_DCS_READ2_RESP:
dbuf->data_type = parameter[0];
dbuf->length = 2;
memcpy(dbuf->data, &parameter[1], dbuf->length);
break;
case DSI_DI_GEN_LREAD_RESP:
case DSI_DI_DCS_LREAD_RESP:
dbuf->data_type = parameter[0];
dbuf->length = (parameter[2] << 8) | parameter[1];
memcpy(dbuf->data, &parameter[4], dbuf->length);
break;
}
}
void dsi_set_dphy(struct mmp_disp_info *fbi)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
int ui, lpx_clk, lpx_time, ta_get, ta_go, wakeup, reg;
int hs_prep, hs_zero, hs_trail, hs_exit, ck_zero, ck_trail, ck_exit;
void *phy_timing0_reg, *phy_timing1_reg,
*phy_timing2_reg, *phy_timing3_reg;
mi->sclk_src /= 1000000;
ui = 1000/mi->sclk_src + 1;
lpx_clk = (phy.lpx_constant + phy.lpx_ui * ui) / DSI_ESC_CLK_T + 1;
lpx_time = lpx_clk * DSI_ESC_CLK_T;
/* Below is for NT35451 */
ta_get = lpx_time * 5 / DSI_ESC_CLK_T - 1;
ta_go = lpx_time * 4 / DSI_ESC_CLK_T - 1;
wakeup = phy.wakeup_constant;
wakeup = wakeup / DSI_ESC_CLK_T + 1;
hs_prep = phy.hs_prep_constant + phy.hs_prep_ui * ui;
hs_prep = hs_prep / DSI_ESC_CLK_T + 1;
/* Our hardware added 3-byte clk automatically.
* 3-byte 3 * 8 * ui.
*/
hs_zero = phy.hs_zero_constant + phy.hs_zero_ui * ui -
(hs_prep + 1) * DSI_ESC_CLK_T;
hs_zero = (hs_zero - (3 * ui << 3)) / DSI_ESC_CLK_T + 4;
if (hs_zero < 0)
hs_zero = 0;
hs_trail = phy.hs_trail_constant + phy.hs_trail_ui * ui;
hs_trail = ((8 * ui) >= hs_trail) ? (8 * ui) : hs_trail;
hs_trail = hs_trail / DSI_ESC_CLK_T + 1;
if (hs_trail > 3)
hs_trail -= 3;
else
hs_trail = 0;
hs_exit = phy.hs_exit_constant + phy.hs_exit_ui * ui;
hs_exit = hs_exit / DSI_ESC_CLK_T + 1;
ck_zero = phy.ck_zero_constant + phy.ck_zero_ui * ui -
(hs_prep + 1) * DSI_ESC_CLK_T;
ck_zero = ck_zero / DSI_ESC_CLK_T + 1;
ck_trail = phy.ck_trail_constant + phy.ck_trail_ui * ui;
ck_trail = ck_trail / DSI_ESC_CLK_T + 1;
ck_exit = hs_exit;
/* bandgap ref enabled by default */
if (!DISP_GEN4(fbi->mi->version)) {
reg = readl(&dsi->phy_rcomp0);
reg |= DPHY_BG_VREF_EN;
phy_timing0_reg = &dsi->phy_timing0;
phy_timing1_reg = &dsi->phy_timing1;
phy_timing2_reg = &dsi->phy_timing2;
phy_timing3_reg = &dsi->phy_timing3;
writel(reg, &dsi->phy_rcomp0);
} else {
reg = readl(&dsi->phy.phy_rcomp0);
reg |= DPHY_BG_VREF_EN_DC4;
phy_timing0_reg = &dsi->phy.phy_timing0;
phy_timing1_reg = &dsi->phy.phy_timing1;
phy_timing2_reg = &dsi->phy.phy_timing2;
phy_timing3_reg = &dsi->phy.phy_timing3;
writel(reg, &dsi->phy.phy_rcomp0);
}
/* timing_0 */
reg = (hs_exit << DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_SHIFT)
| (hs_trail << DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_SHIFT)
| (hs_zero << DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_SHIFT)
| (hs_prep);
writel(reg, phy_timing0_reg);
reg = (ta_get << DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_SHIFT)
| (ta_go << DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_SHIFT)
| wakeup;
writel(reg, phy_timing1_reg);
reg = (ck_exit << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_SHIFT)
| (ck_trail << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_SHIFT)
| (ck_zero << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_SHIFT)
| lpx_clk;
writel(reg, phy_timing2_reg);
reg = (lpx_clk << DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_SHIFT)
| phy.req_ready;
writel(reg, phy_timing3_reg);
/* calculated timing on brownstone:
* DSI_PHY_TIME_0 0x06080204
* DSI_PHY_TIME_1 0x6d2bfff0
* DSI_PHY_TIME_2 0x603130a
* DSI_PHY_TIME_3 0xa3c
*/
}
#if defined(DEBUG_MMP_DISP)
#define DSIW(x, y) do {writel(x, y); \
printf("address %x, value %x\n", y, x); \
} while (0)
#else
#define DSIW(x, y) writel(x, y)
#endif
static void dsi_set_ctrl0(struct dsi_regs *dsi,
u32 mask, u32 set)
{
u32 tmp;
tmp = readl(&dsi->ctrl0);
tmp &= ~mask;
tmp |= set;
DSIW(tmp, &dsi->ctrl0);
}
void dsi_reset(struct dsi_info *di, int hold)
{
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
/* software reset DSI module */
dsi_set_ctrl0(dsi, 0, DSI_CTRL_0_CFG_SOFT_RST);
/* Note: there need some delay after set DSI_CTRL_0_CFG_SOFT_RST */
udelay(1000);
dsi_set_ctrl0(dsi, (u32)DSI_CTRL_0_CFG_LCD1_EN,
(u32)DSI_CTRL_0_CFG_SOFT_RST_REG);
if (!hold) {
dsi_set_ctrl0(dsi, DSI_CTRL_0_CFG_SOFT_RST, 0);
udelay(1000);
dsi_set_ctrl0(dsi, DSI_CTRL_0_CFG_SOFT_RST_REG, 0);
}
}
void dsi_set_video_panel(struct mmp_disp_info *fbi)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
struct dsi_lcd_regs *dsi_lcd = &dsi->lcd1;
unsigned hsync_b, hbp_b, hact_b, hex_b, hfp_b, httl_b;
unsigned hsync, hbp, hact, hfp, httl, v_total;
unsigned hsa_wc, hbp_wc, hact_wc, hex_wc, hfp_wc, hlp_wc;
int bpp = di->bpp, hss_bcnt = 4, hse_bct = 4, lgp_over_head = 6, reg;
int slot_cnt0, slot_cnt1;
void *phy_ctrl2_reg = (!DISP_GEN4(fbi->mi->version)) ?
(&dsi->phy_ctrl2) : (&dsi->phy.phy_ctrl2);
if (di->id & 2)
dsi_lcd = &dsi->lcd2;
v_total = mi->modes->yres + mi->modes->upper_margin + mi->modes->lower_margin
+ mi->modes->vsync_len;
hact_b = to_dsi_bcnt(mi->modes->xres, bpp);
hfp_b = to_dsi_bcnt(mi->modes->right_margin, bpp);
hbp_b = to_dsi_bcnt(mi->modes->left_margin, bpp);
hsync_b = to_dsi_bcnt(mi->modes->hsync_len, bpp);
hex_b = to_dsi_bcnt(dsi_ex_pixel_cnt, bpp);
httl_b = hact_b + hsync_b + hfp_b + hbp_b + hex_b;
slot_cnt0 = httl_b - hact_b + 3;
slot_cnt1 = slot_cnt0;
hact = hact_b / di->lanes;
hfp = hfp_b / di->lanes;
hbp = hbp_b / di->lanes;
hsync = hsync_b / di->lanes;
httl = hact + hfp + hbp + hsync;
/* word count in the unit of byte */
hsa_wc = (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ?
(hsync_b - hss_bcnt - lgp_over_head) : 0;
/* Hse is with backporch */
hbp_wc = (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ?
(hbp_b - hse_bct - lgp_over_head)
: (hsync_b + hbp_b - hss_bcnt - lgp_over_head);
hfp_wc = ((di->burst_mode == DSI_BURST_MODE_BURST) && (dsi_hex_en == 0)) ?
(hfp_b + hex_b - lgp_over_head - lgp_over_head) :
(hfp_b - lgp_over_head - lgp_over_head);
hact_wc = ((mi->modes->xres) * bpp) >> 3;
/* disable Hex currently */
hex_wc = 0;
/* There is no hlp with active data segment. */
hlp_wc = (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ?
(httl_b - hsync_b - hse_bct - lgp_over_head) :
(httl_b - hss_bcnt - lgp_over_head);
/* FIXME - need to double check the (*3) is bytes_per_pixel
* from input data or output to panel
*/
/* dsi_lane_enable - Set according to specified DSI lane count */
DSIW(dsi_lane[di->lanes] << DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_SHIFT,
phy_ctrl2_reg);
DSIW(dsi_lane[di->lanes] << DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT,
&dsi->cmd1);
/* SET UP LCD1 TIMING REGISTERS FOR DSI BUS */
/* NOTE: Some register values were obtained by trial and error */
DSIW((hact << 16) | httl, &dsi_lcd->timing0);
DSIW((hsync << 16) | hbp, &dsi_lcd->timing1);
/*
* For now the active size is set really low (we'll use 10) to allow
* the hardware to attain V Sync. Once the DSI bus is up and running,
* the final value will be put in place for the active size (this is
* done below). In a later stepping of the processor this requirement
* will not be required.
*/
DSIW(((mi->modes->yres)<<16) | (v_total), &dsi_lcd->timing2);
DSIW(((mi->modes->vsync_len) << 16) | (mi->modes->upper_margin),
&dsi_lcd->timing3);
/* SET UP LCD1 WORD COUNT REGISTERS FOR DSI BUS */
/* Set up for word(byte) count register 0 */
DSIW((hbp_wc << 16) | hsa_wc, &dsi_lcd->wc0);
DSIW((hfp_wc << 16) | hact_wc, &dsi_lcd->wc1);
DSIW((hex_wc << 16) | hlp_wc, &dsi_lcd->wc2);
/* calculated value on brownstone:
* WC0: 0x1a0000
* WC1: 0x1500f00
* WC2: 0x1076 */
DSIW((slot_cnt0 << 16) | slot_cnt0, &dsi_lcd->slot_cnt0);
DSIW((slot_cnt1 << 16) | slot_cnt1, &dsi_lcd->slot_cnt1);
/* Configure LCD control register 1 FOR DSI BUS */
reg = ((di->rgb_mode << DSI_LCD2_CTRL_1_CFG_L1_RGB_TYPE_SHIFT)
| (di->burst_mode << DSI_LCD1_CTRL_1_CFG_L1_BURST_MODE_SHIFT)
| (di->lpm_line_en ? DSI_LCD1_CTRL_1_CFG_L1_LPM_LINE_EN : 0)
| (di->lpm_frame_en ? DSI_LCD1_CTRL_1_CFG_L1_LPM_FRAME_EN : 0)
| (di->last_line_turn ? DSI_LCD1_CTRL_1_CFG_L1_LAST_LINE_TURN
: 0)
| (di->hex_slot_en ? 0 : 0) /* disable Hex slot */
| (di->hbp_en ? DSI_LCD1_CTRL_1_CFG_L1_HBP_PKT_EN : 0)
| (di->hfp_en ? DSI_LCD1_CTRL_1_CFG_L1_HFP_PKT_EN : 0)
| (di->hex_en ? 0 : 0) /* Hex packet is disabled */
| (di->hlp_en ? DSI_LCD1_CTRL_1_CFG_L1_HLP_PKT_EN : 0));
reg |= (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ?
(((di->hsa_en) ? DSI_LCD1_CTRL_1_CFG_L1_HSA_PKT_EN : 0)
| (DSI_LCD1_CTRL_1_CFG_L1_HSE_PKT_EN)) /* Hse is always
eabled */
:
(((di->hsa_en) ? 0 : 0) /* Hsa packet is disabled */
| ((di->hse_en) ? 0 : 0)); /* Hse packet is disabled */
if (DISP_GEN4(fbi->mi->version)) {
/*
* DC4 dsi_lcd ctrl1 add below bits:
* 1. word count auto calculation.
* 2. check timing before request DPHY for TX.
* 3. auto vsync delay count calculation.
*
* According to DE, if we want to lpm_line,
* we can configure hfp_en/hbp_en.
*/
reg |= (di->lpm_line_en ? (DSI_LCD1_CTRL_1_CFG_L1_HFP_PKT_EN
| DSI_LCD1_CTRL_1_CFG_L1_HBP_PKT_EN)
: 0);
} else {
reg |= (di->lpm_line_en ?
DSI_LCD1_CTRL_1_CFG_L1_LPM_LINE_EN : 0)
| (di->hact_en ? DSI_LCD1_CTRL_1_CFG_L1_HACT_PKT_EN : 0)
| (di->all_slot_en ? 0 : 0); /* disable all slots */
}
reg |= DSI_LCD1_CTRL_1_CFG_L1_VSYNC_RST_EN;
DSIW(reg, &dsi_lcd->ctrl1);
/*Start the transfer of LCD data over the DSI bus*/
/* DSI_CTRL_1 */
reg = readl(&dsi->ctrl1);
reg &= ~(DSI_CTRL_1_CFG_LCD2_VCH_NO_MASK
| DSI_CTRL_1_CFG_LCD1_VCH_NO_MASK);
reg |= 0x1 << ((di->id & 1) ? DSI_CTRL_1_CFG_LCD2_VCH_NO_SHIFT
: DSI_CTRL_1_CFG_LCD1_VCH_NO_SHIFT);
reg &= ~(DSI_CTRL_1_CFG_EOTP);
if (di->eotp_en)
reg |= DSI_CTRL_1_CFG_EOTP; /* EOTP */
DSIW(reg, &dsi->ctrl1);
/* DSI_CTRL_0 */
reg = (di->master_mode ? 0 : DSI_CTRL_0_CFG_LCD1_SLV) |
DSI_CTRL_0_CFG_LCD1_TX_EN | DSI_CTRL_0_CFG_LCD1_EN;
if (di->id & 2)
reg = reg << 1;
dsi_set_ctrl0(dsi, DSI_CTRL_0_CFG_LCD1_SLV, reg);
udelay(100000);
}
void set_dsi_low_power_mode(struct mmp_disp_info *fbi)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
void *phy_ctrl2_reg = (!DISP_GEN4(fbi->mi->version)) ?
(&dsi->phy_ctrl2) : (&dsi->phy.phy_ctrl2);
u32 reg = readl(phy_ctrl2_reg);
/* enable data lane data0*/
reg &= ~(0xf << 4);
reg |= (1 << 4);
writel(reg, phy_ctrl2_reg);
/* LPDT TX enabled for data0 */
reg = readl(&dsi->cmd1);
reg &= ~(0xf << 20);
reg |= 1 << DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT;
writel(reg, &dsi->cmd1);
}
/*
* FIXME: The read id commands is only for sharp panel.
*/
#define SHARP_NO_DELAY 0
static u8 pkt_size_cmd[] = {0x1};
static u8 read_id1[] = {0xda};
static u8 read_id2[] = {0xdb};
static u8 read_id3[] = {0xdc};
static struct dsi_cmd_desc sharp_read_id1_cmds[] = {
{DSI_DI_SET_MAX_PKT_SIZE, 1, SHARP_NO_DELAY, sizeof(pkt_size_cmd),
pkt_size_cmd},
{DSI_DI_DCS_READ, 1, SHARP_NO_DELAY, sizeof(read_id1), read_id1},
};
static struct dsi_cmd_desc sharp_read_id2_cmds[] = {
{DSI_DI_SET_MAX_PKT_SIZE, 1, SHARP_NO_DELAY, sizeof(pkt_size_cmd),
pkt_size_cmd},
{DSI_DI_DCS_READ, 1, SHARP_NO_DELAY, sizeof(read_id2), read_id2},
};
static struct dsi_cmd_desc sharp_read_id3_cmds[] = {
{DSI_DI_SET_MAX_PKT_SIZE, 1, SHARP_NO_DELAY, sizeof(pkt_size_cmd),
pkt_size_cmd},
{DSI_DI_DCS_READ, 1, SHARP_NO_DELAY, sizeof(read_id3), read_id3},
};
static int panel_read_id(struct mmp_disp_info *fbi)
{
struct dsi_buf *dbuf;
u32 read_id = 0;
dbuf = malloc(sizeof(struct dsi_buf));
if (dbuf) {
dsi_cmd_array_rx(fbi, dbuf, sharp_read_id1_cmds,
ARRAY_SIZE(sharp_read_id1_cmds));
read_id |= dbuf->data[0] << 16;
dsi_cmd_array_rx(fbi, dbuf, sharp_read_id2_cmds,
ARRAY_SIZE(sharp_read_id2_cmds));
read_id |= dbuf->data[0] << 8;
dsi_cmd_array_rx(fbi, dbuf, sharp_read_id3_cmds,
ARRAY_SIZE(sharp_read_id3_cmds));
read_id |= dbuf->data[0];
free(dbuf);
printf("panel id is 0x%x\n", read_id);
return read_id;
} else {
printf("%s: can't alloc dsi rx buffer\n", __func__);
return -1;
}
}
static void dsi_set_dphy_ctrl1(struct dsi_info *di,
u32 mask, u32 set, unsigned version)
{
struct dsi_regs *dsi = (struct dsi_regs *)(uintptr_t)di->regs;
void *phy_ctrl1_reg = (!DISP_GEN4(version)) ?
(&dsi->phy_ctrl1) : (&dsi->phy.phy_ctrl1);
u32 tmp;
tmp = readl(phy_ctrl1_reg);
tmp &= ~mask;
tmp |= set;
writel(tmp, phy_ctrl1_reg);
}
int mmp_disp_dsi_init(struct mmp_disp_info *fbi)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
int ret = 0;
/* reset and de-assert DSI controller */
dsi_reset(di, 0);
/* digital and analog power on */
dsi_set_dphy_ctrl1(di, 0x0, DPHY_CFG_VDD_VALID,
fbi->mi->version);
/* turn on DSI continuous clock for HS */
dsi_set_dphy_ctrl1(di, 0x0, 0x1,
fbi->mi->version);
/* set dphy */
dsi_set_dphy(fbi);
/*
* FIXME: set cmds in LP only need enbale lane0,
* if neable all lanes, will fail.
*/
if (mi->phy_type == DSI) {
/*
* If dynamic_detect_panel flag is set and
* detected panel id does not match the default one,
* need to reset panel related info
* and do the intial flow again.
*/
if (mi->dynamic_detect_panel == 1) {
g_panel_id = panel_read_id(fbi);
if (mi->panel_manufacturer_id != g_panel_id)
return -1;
}
}
/* put all lanes to LP-11 state */
dsi_lanes_enable(di, 0, fbi->mi->version);
dsi_lanes_enable(di, 1, fbi->mi->version);
/* init panel settings via dsi */
if (mi->phy_type == DSI)
mi->dsi_panel_config(fbi);
/* reset the bridge */
if (mi->phy_type == DSI2DPI) {
if (mi->xcvr_reset)
mi->xcvr_reset();
else
tc358765_reset();
}
/* set dsi controller */
dsi_set_video_panel(fbi);
/* set dsi to dpi conversion chip */
if (mi->phy_type == DSI2DPI) {
ret = dsi_set_tc358765(fbi);
if (ret < 0)
printf("dsi2dpi_set error!\n");
}
return 0;
}