M7350/kernel/drivers/video/msm/mdss/dsi_host_v2.c
2024-09-09 08:52:07 +00:00

996 lines
24 KiB
C

/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include "dsi_v2.h"
#include "dsi_io_v2.h"
#include "dsi_host_v2.h"
#define DSI_POLL_SLEEP_US 1000
#define DSI_POLL_TIMEOUT_US 16000
#define DSI_ESC_CLK_RATE 19200000
#define DSI_DMA_CMD_TIMEOUT_MS 200
struct dsi_host_v2_private {
struct completion dma_comp;
int irq_enabled;
spinlock_t irq_lock;
int irq_no;
unsigned char *dsi_base;
struct device dis_dev;
};
static struct dsi_host_v2_private *dsi_host_private;
int msm_dsi_init(void)
{
if (!dsi_host_private) {
dsi_host_private = kzalloc(sizeof(struct dsi_host_v2_private),
GFP_KERNEL);
if (!dsi_host_private) {
pr_err("fail to alloc dsi host private data\n");
return -ENOMEM;
}
}
init_completion(&dsi_host_private->dma_comp);
spin_lock_init(&dsi_host_private->irq_lock);
return 0;
}
void msm_dsi_deinit(void)
{
kfree(dsi_host_private);
dsi_host_private = NULL;
}
void msm_dsi_ack_err_status(unsigned char *ctrl_base)
{
u32 status;
status = MIPI_INP(ctrl_base + DSI_ACK_ERR_STATUS);
if (status) {
MIPI_OUTP(ctrl_base + DSI_ACK_ERR_STATUS, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
void msm_dsi_timeout_status(unsigned char *ctrl_base)
{
u32 status;
status = MIPI_INP(ctrl_base + DSI_TIMEOUT_STATUS);
if (status & 0x0111) {
MIPI_OUTP(ctrl_base + DSI_TIMEOUT_STATUS, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
void msm_dsi_dln0_phy_err(unsigned char *ctrl_base)
{
u32 status;
status = MIPI_INP(ctrl_base + DSI_DLN0_PHY_ERR);
if (status & 0x011111) {
MIPI_OUTP(ctrl_base + DSI_DLN0_PHY_ERR, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
void msm_dsi_fifo_status(unsigned char *ctrl_base)
{
u32 status;
status = MIPI_INP(ctrl_base + DSI_FIFO_STATUS);
if (status & 0x44444489) {
MIPI_OUTP(ctrl_base + DSI_FIFO_STATUS, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
void msm_dsi_status(unsigned char *ctrl_base)
{
u32 status;
status = MIPI_INP(ctrl_base + DSI_STATUS);
if (status & 0x80000000) {
MIPI_OUTP(ctrl_base + DSI_STATUS, status);
pr_debug("%s: status=%x\n", __func__, status);
}
}
void msm_dsi_error(unsigned char *ctrl_base)
{
msm_dsi_ack_err_status(ctrl_base);
msm_dsi_timeout_status(ctrl_base);
msm_dsi_fifo_status(ctrl_base);
msm_dsi_status(ctrl_base);
msm_dsi_dln0_phy_err(ctrl_base);
}
void msm_dsi_enable_irq(void)
{
unsigned long flags;
spin_lock_irqsave(&dsi_host_private->irq_lock, flags);
dsi_host_private->irq_enabled++;
if (dsi_host_private->irq_enabled == 1)
enable_irq(dsi_host_private->irq_no);
spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
}
void msm_dsi_disable_irq(void)
{
unsigned long flags;
spin_lock_irqsave(&dsi_host_private->irq_lock, flags);
dsi_host_private->irq_enabled--;
if (dsi_host_private->irq_enabled == 0)
disable_irq(dsi_host_private->irq_no);
spin_unlock_irqrestore(&dsi_host_private->irq_lock, flags);
}
void msm_dsi_disable_irq_nosync(void)
{
spin_lock(&dsi_host_private->irq_lock);
dsi_host_private->irq_enabled--;
if (dsi_host_private->irq_enabled == 0)
disable_irq_nosync(dsi_host_private->irq_no);
spin_unlock(&dsi_host_private->irq_lock);
}
irqreturn_t msm_dsi_isr(int irq, void *ptr)
{
u32 isr;
isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
if (isr & DSI_INTR_ERROR)
msm_dsi_error(dsi_host_private->dsi_base);
if (isr & DSI_INTR_CMD_DMA_DONE)
complete(&dsi_host_private->dma_comp);
return IRQ_HANDLED;
}
int msm_dsi_irq_init(struct device *dev, int irq_no)
{
int ret;
ret = devm_request_irq(dev, irq_no, msm_dsi_isr,
IRQF_DISABLED, "DSI", NULL);
if (ret) {
pr_err("msm_dsi_irq_init request_irq() failed!\n");
return ret;
}
dsi_host_private->irq_no = irq_no;
disable_irq(irq_no);
return 0;
}
void msm_dsi_host_init(struct mipi_panel_info *pinfo)
{
u32 dsi_ctrl, intr_ctrl, data;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
pr_debug("msm_dsi_host_init\n");
pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
if (pinfo->mode == DSI_VIDEO_MODE) {
data = 0;
if (pinfo->pulse_mode_hsa_he)
data |= BIT(28);
if (pinfo->hfp_power_stop)
data |= BIT(24);
if (pinfo->hbp_power_stop)
data |= BIT(20);
if (pinfo->hsa_power_stop)
data |= BIT(16);
if (pinfo->eof_bllp_power_stop)
data |= BIT(15);
if (pinfo->bllp_power_stop)
data |= BIT(12);
data |= ((pinfo->traffic_mode & 0x03) << 8);
data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */
data |= (pinfo->vc & 0x03);
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_CTRL, data);
data = 0;
data |= ((pinfo->rgb_swap & 0x07) << 12);
if (pinfo->b_sel)
data |= BIT(8);
if (pinfo->g_sel)
data |= BIT(4);
if (pinfo->r_sel)
data |= BIT(0);
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_DATA_CTRL, data);
} else if (pinfo->mode == DSI_CMD_MODE) {
data = 0;
data |= ((pinfo->interleave_max & 0x0f) << 20);
data |= ((pinfo->rgb_swap & 0x07) << 16);
if (pinfo->b_sel)
data |= BIT(12);
if (pinfo->g_sel)
data |= BIT(8);
if (pinfo->r_sel)
data |= BIT(4);
data |= (pinfo->dst_format & 0x0f); /* 4 bits */
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_CTRL, data);
/* DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL */
data = pinfo->wr_mem_continue & 0x0ff;
data <<= 8;
data |= (pinfo->wr_mem_start & 0x0ff);
if (pinfo->insert_dcs_cmd)
data |= BIT(16);
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL,
data);
} else
pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode);
dsi_ctrl = BIT(8) | BIT(2); /* clock enable & cmd mode */
intr_ctrl = 0;
intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_CMD_MDP_DONE_MASK);
if (pinfo->crc_check)
dsi_ctrl |= BIT(24);
if (pinfo->ecc_check)
dsi_ctrl |= BIT(20);
if (pinfo->data_lane3)
dsi_ctrl |= BIT(7);
if (pinfo->data_lane2)
dsi_ctrl |= BIT(6);
if (pinfo->data_lane1)
dsi_ctrl |= BIT(5);
if (pinfo->data_lane0)
dsi_ctrl |= BIT(4);
/* from frame buffer, low power mode */
/* DSI_COMMAND_MODE_DMA_CTRL */
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, 0x14000000);
data = 0;
if (pinfo->te_sel)
data |= BIT(31);
data |= pinfo->mdp_trigger << 4;/* cmd mdp trigger */
data |= pinfo->dma_trigger; /* cmd dma trigger */
data |= (pinfo->stream & 0x01) << 8;
MIPI_OUTP(ctrl_base + DSI_TRIG_CTRL, data);
/* DSI_LAN_SWAP_CTRL */
MIPI_OUTP(ctrl_base + DSI_LANE_SWAP_CTRL, pinfo->dlane_swap);
/* clock out ctrl */
data = pinfo->t_clk_post & 0x3f; /* 6 bits */
data <<= 8;
data |= pinfo->t_clk_pre & 0x3f; /* 6 bits */
/* DSI_CLKOUT_TIMING_CTRL */
MIPI_OUTP(ctrl_base + DSI_CLKOUT_TIMING_CTRL, data);
data = 0;
if (pinfo->rx_eot_ignore)
data |= BIT(4);
if (pinfo->tx_eot_append)
data |= BIT(0);
MIPI_OUTP(ctrl_base + DSI_EOT_PACKET_CTRL, data);
/* allow only ack-err-status to generate interrupt */
/* DSI_ERR_INT_MASK0 */
MIPI_OUTP(ctrl_base + DSI_ERR_INT_MASK0, 0x13ff3fe0);
intr_ctrl |= DSI_INTR_ERROR_MASK;
MIPI_OUTP(ctrl_base + DSI_INT_CTRL, intr_ctrl);
/* turn esc, byte, dsi, pclk, sclk, hclk on */
MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0x23f);
dsi_ctrl |= BIT(0); /* enable dsi */
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
wmb();
}
void msm_dsi_set_tx_power_mode(int mode)
{
u32 data;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
data = MIPI_INP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL);
if (mode == 0)
data &= ~BIT(26);
else
data |= BIT(26);
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, data);
}
void msm_dsi_sw_reset(void)
{
u32 dsi_ctrl;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
pr_debug("msm_dsi_sw_reset\n");
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
dsi_ctrl &= ~0x01;
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
wmb();
/* turn esc, byte, dsi, pclk, sclk, hclk on */
MIPI_OUTP(ctrl_base + DSI_CLK_CTRL, 0x23f);
wmb();
MIPI_OUTP(ctrl_base + DSI_SOFT_RESET, 0x01);
wmb();
MIPI_OUTP(ctrl_base + DSI_SOFT_RESET, 0x00);
wmb();
}
void msm_dsi_controller_cfg(int enable)
{
u32 dsi_ctrl, status;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
pr_debug("msm_dsi_controller_cfg\n");
/* Check for CMD_MODE_DMA_BUSY */
if (readl_poll_timeout((ctrl_base + DSI_STATUS),
status,
((status & 0x02) == 0),
DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US))
pr_err("%s: DSI status=%x failed\n", __func__, status);
/* Check for x_HS_FIFO_EMPTY */
if (readl_poll_timeout((ctrl_base + DSI_FIFO_STATUS),
status,
((status & 0x11111000) == 0x11111000),
DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US))
pr_err("%s: FIFO status=%x failed\n", __func__, status);
/* Check for VIDEO_MODE_ENGINE_BUSY */
if (readl_poll_timeout((ctrl_base + DSI_STATUS),
status,
((status & 0x08) == 0),
DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) {
pr_err("%s: DSI status=%x\n", __func__, status);
pr_err("%s: Doing sw reset\n", __func__);
msm_dsi_sw_reset();
}
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
if (enable)
dsi_ctrl |= 0x01;
else
dsi_ctrl &= ~0x01;
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
wmb();
}
void msm_dsi_op_mode_config(int mode, struct mdss_panel_data *pdata)
{
u32 dsi_ctrl, intr_ctrl;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
pr_debug("msm_dsi_op_mode_config\n");
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
/*If Video enabled, Keep Video and Cmd mode ON */
dsi_ctrl &= ~0x06;
if (mode == DSI_VIDEO_MODE) {
dsi_ctrl |= 0x02;
intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK;
} else { /* command mode */
dsi_ctrl |= 0x04;
intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK |
DSI_INTR_CMD_MDP_DONE_MASK;
}
pr_debug("%s: dsi_ctrl=%x intr=%x\n", __func__, dsi_ctrl, intr_ctrl);
MIPI_OUTP(ctrl_base + DSI_INT_CTRL, intr_ctrl);
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
wmb();
}
int msm_dsi_cmd_reg_tx(u32 data)
{
unsigned char *ctrl_base = dsi_host_private->dsi_base;
MIPI_OUTP(ctrl_base + DSI_TRIG_CTRL, 0x04);/* sw trigger */
MIPI_OUTP(ctrl_base + DSI_CTRL, 0x135);
wmb();
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_DMA_CTRL, data);
wmb();
MIPI_OUTP(ctrl_base + DSI_CMD_MODE_DMA_SW_TRIGGER, 0x01);
wmb();
udelay(300); /*per spec*/
return 0;
}
int msm_dsi_cmd_dma_tx(struct dsi_buf *tp)
{
int len, rc;
unsigned long size, addr;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
len = ALIGN(tp->len, 4);
size = ALIGN(tp->len, SZ_4K);
tp->dmap = dma_map_single(&dsi_host_private->dis_dev, tp->data, size,
DMA_TO_DEVICE);
if (dma_mapping_error(&dsi_host_private->dis_dev, tp->dmap)) {
pr_err("%s: dmap mapp failed\n", __func__);
return -ENOMEM;
}
addr = tp->dmap;
INIT_COMPLETION(dsi_host_private->dma_comp);
MIPI_OUTP(ctrl_base + DSI_DMA_CMD_OFFSET, addr);
MIPI_OUTP(ctrl_base + DSI_DMA_CMD_LENGTH, len);
wmb();
MIPI_OUTP(ctrl_base + DSI_CMD_MODE_DMA_SW_TRIGGER, 0x01);
wmb();
rc = wait_for_completion_timeout(&dsi_host_private->dma_comp,
msecs_to_jiffies(DSI_DMA_CMD_TIMEOUT_MS));
if (rc == 0) {
pr_err("DSI command transaction time out\n");
rc = -ETIME;
}
dma_unmap_single(&dsi_host_private->dis_dev, tp->dmap, size,
DMA_TO_DEVICE);
tp->dmap = 0;
return rc;
}
int msm_dsi_cmd_dma_rx(struct dsi_buf *rp, int rlen)
{
u32 *lp, data;
int i, off, cnt;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
lp = (u32 *)rp->data;
cnt = rlen;
cnt += 3;
cnt >>= 2;
if (cnt > 4)
cnt = 4; /* 4 x 32 bits registers only */
off = DSI_RDBK_DATA0;
off += ((cnt - 1) * 4);
for (i = 0; i < cnt; i++) {
data = (u32)MIPI_INP(ctrl_base + off);
*lp++ = ntohl(data); /* to network byte order */
pr_debug("%s: data = 0x%x and ntohl(data) = 0x%x\n",
__func__, data, ntohl(data));
off -= 4;
rp->len += sizeof(*lp);
}
return 0;
}
int msm_dsi_cmds_tx(struct mdss_panel_data *pdata,
struct dsi_buf *tp, struct dsi_cmd_desc *cmds, int cnt)
{
struct dsi_cmd_desc *cm;
u32 dsi_ctrl, ctrl;
int i, video_mode;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
/* turn on cmd mode
* for video mode, do not send cmds more than
* one pixel line, since it only transmit it
* during BLLP.
*/
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */
if (video_mode) {
ctrl = dsi_ctrl | 0x04; /* CMD_MODE_EN */
MIPI_OUTP(ctrl_base + DSI_CTRL, ctrl);
}
msm_dsi_enable_irq();
cm = cmds;
for (i = 0; i < cnt; i++) {
dsi_buf_init(tp);
dsi_cmd_dma_add(tp, cm);
msm_dsi_cmd_dma_tx(tp);
if (cm->wait)
msleep(cm->wait);
cm++;
}
msm_dsi_disable_irq();
if (video_mode)
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
return 0;
}
/* MDSS_DSI_MRPS, Maximum Return Packet Size */
static char max_pktsize[2] = {0x00, 0x00}; /* LSB tx first, 10 bytes */
static struct dsi_cmd_desc pkt_size_cmd[] = {
{DTYPE_MAX_PKTSIZE, 1, 0, 0, 0,
sizeof(max_pktsize), max_pktsize}
};
/*
* DSI panel reply with MAX_RETURN_PACKET_SIZE bytes of data
* plus DCS header, ECC and CRC for DCS long read response
* mdss_dsi_controller only have 4x32 bits register ( 16 bytes) to
* hold data per transaction.
* MDSS_DSI_LEN equal to 8
* len should be either 4 or 8
* any return data more than MDSS_DSI_LEN need to be break down
* to multiple transactions.
*
* ov_mutex need to be acquired before call this function.
*/
int msm_dsi_cmds_rx(struct mdss_panel_data *pdata,
struct dsi_buf *tp, struct dsi_buf *rp,
struct dsi_cmd_desc *cmds, int rlen)
{
int cnt, len, diff, pkt_size;
char cmd;
if (pdata->panel_info.mipi.no_max_pkt_size)
rlen = ALIGN(rlen, 4); /* Only support rlen = 4*n */
len = rlen;
diff = 0;
if (len <= 2) {
cnt = 4; /* short read */
} else {
if (len > DSI_LEN)
len = DSI_LEN; /* 8 bytes at most */
len = ALIGN(len, 4); /* len 4 bytes align */
diff = len - rlen;
/*
* add extra 2 bytes to len to have overall
* packet size is multipe by 4. This also make
* sure 4 bytes dcs headerlocates within a
* 32 bits register after shift in.
* after all, len should be either 6 or 10.
*/
len += 2;
cnt = len + 6; /* 4 bytes header + 2 bytes crc */
}
msm_dsi_enable_irq();
if (!pdata->panel_info.mipi.no_max_pkt_size) {
/* packet size need to be set at every read */
pkt_size = len;
max_pktsize[0] = pkt_size;
dsi_buf_init(tp);
dsi_cmd_dma_add(tp, pkt_size_cmd);
msm_dsi_cmd_dma_tx(tp);
pr_debug("%s: Max packet size sent\n", __func__);
}
dsi_buf_init(tp);
dsi_cmd_dma_add(tp, cmds);
/* transmit read comamnd to client */
msm_dsi_cmd_dma_tx(tp);
/*
* once cmd_dma_done interrupt received,
* return data from client is ready and stored
* at RDBK_DATA register already
*/
dsi_buf_init(rp);
if (pdata->panel_info.mipi.no_max_pkt_size) {
/*
* expect rlen = n * 4
* short alignement for start addr
*/
rp->data += 2;
}
msm_dsi_cmd_dma_rx(rp, cnt);
msm_dsi_disable_irq();
if (pdata->panel_info.mipi.no_max_pkt_size) {
/*
* remove extra 2 bytes from previous
* rx transaction at shift register
* which was inserted during copy
* shift registers to rx buffer
* rx payload start from long alignment addr
*/
rp->data += 2;
}
cmd = rp->data[0];
switch (cmd) {
case DTYPE_ACK_ERR_RESP:
pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
break;
case DTYPE_GEN_READ1_RESP:
case DTYPE_DCS_READ1_RESP:
dsi_short_read1_resp(rp);
break;
case DTYPE_GEN_READ2_RESP:
case DTYPE_DCS_READ2_RESP:
dsi_short_read2_resp(rp);
break;
case DTYPE_GEN_LREAD_RESP:
case DTYPE_DCS_LREAD_RESP:
dsi_long_read_resp(rp);
rp->len -= 2; /* extra 2 bytes added */
rp->len -= diff; /* align bytes */
break;
default:
pr_debug("%s: Unknown cmd received\n", __func__);
break;
}
return rp->len;
}
static int msm_dsi_cal_clk_rate(struct mdss_panel_data *pdata,
u32 *bitclk_rate,
u32 *dsiclk_rate,
u32 *byteclk_rate,
u32 *pclk_rate)
{
struct mdss_panel_info *pinfo;
struct mipi_panel_info *mipi;
u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height;
int lanes;
pinfo = &pdata->panel_info;
mipi = &pdata->panel_info.mipi;
hbp = pdata->panel_info.lcdc.h_back_porch;
hfp = pdata->panel_info.lcdc.h_front_porch;
vbp = pdata->panel_info.lcdc.v_back_porch;
vfp = pdata->panel_info.lcdc.v_front_porch;
hspw = pdata->panel_info.lcdc.h_pulse_width;
vspw = pdata->panel_info.lcdc.v_pulse_width;
width = pdata->panel_info.xres;
height = pdata->panel_info.yres;
lanes = 0;
if (mipi->data_lane0)
lanes++;
if (mipi->data_lane1)
lanes++;
if (mipi->data_lane2)
lanes++;
if (mipi->data_lane3)
lanes++;
if (lanes == 0)
return -EINVAL;
*bitclk_rate = (width + hbp + hfp + hspw) * (height + vbp + vfp + vspw);
*bitclk_rate *= mipi->frame_rate;
*bitclk_rate *= pdata->panel_info.bpp;
*bitclk_rate /= lanes;
*byteclk_rate = *bitclk_rate / 8;
*dsiclk_rate = *byteclk_rate * lanes;
*pclk_rate = *byteclk_rate * lanes * 8 / pdata->panel_info.bpp;
pr_debug("dsiclk_rate=%u, byteclk=%u, pck_=%u\n",
*dsiclk_rate, *byteclk_rate, *pclk_rate);
return 0;
}
static int msm_dsi_on(struct mdss_panel_data *pdata)
{
int ret = 0;
u32 clk_rate;
struct mdss_panel_info *pinfo;
struct mipi_panel_info *mipi;
u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height;
u32 ystride, bpp, data;
u32 dummy_xres, dummy_yres;
u32 bitclk_rate = 0, byteclk_rate = 0, pclk_rate = 0, dsiclk_rate = 0;
unsigned char *ctrl_base = dsi_host_private->dsi_base;
pr_debug("msm_dsi_on\n");
pinfo = &pdata->panel_info;
ret = msm_dsi_regulator_enable();
if (ret) {
pr_err("%s: DSI power on failed\n", __func__);
return ret;
}
msm_dsi_ahb_ctrl(1);
msm_dsi_phy_sw_reset(dsi_host_private->dsi_base);
msm_dsi_phy_init(dsi_host_private->dsi_base, pdata);
msm_dsi_cal_clk_rate(pdata, &bitclk_rate, &dsiclk_rate,
&byteclk_rate, &pclk_rate);
msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, dsiclk_rate,
byteclk_rate, pclk_rate);
msm_dsi_prepare_clocks();
msm_dsi_clk_enable();
clk_rate = pdata->panel_info.clk_rate;
clk_rate = min(clk_rate, pdata->panel_info.clk_max);
hbp = pdata->panel_info.lcdc.h_back_porch;
hfp = pdata->panel_info.lcdc.h_front_porch;
vbp = pdata->panel_info.lcdc.v_back_porch;
vfp = pdata->panel_info.lcdc.v_front_porch;
hspw = pdata->panel_info.lcdc.h_pulse_width;
vspw = pdata->panel_info.lcdc.v_pulse_width;
width = pdata->panel_info.xres;
height = pdata->panel_info.yres;
mipi = &pdata->panel_info.mipi;
if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
dummy_xres = pdata->panel_info.lcdc.xres_pad;
dummy_yres = pdata->panel_info.lcdc.yres_pad;
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_ACTIVE_H,
((hspw + hbp + width + dummy_xres) << 16 |
(hspw + hbp)));
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_ACTIVE_V,
((vspw + vbp + height + dummy_yres) << 16 |
(vspw + vbp)));
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_TOTAL,
(vspw + vbp + height + dummy_yres +
vfp - 1) << 16 | (hspw + hbp +
width + dummy_xres + hfp - 1));
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_HSYNC, (hspw << 16));
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_VSYNC, 0);
MIPI_OUTP(ctrl_base + DSI_VIDEO_MODE_VSYNC_VPOS,
(vspw << 16));
} else { /* command mode */
if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB888)
bpp = 3;
else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB666)
bpp = 3;
else if (mipi->dst_format == DSI_CMD_DST_FORMAT_RGB565)
bpp = 2;
else
bpp = 3; /* Default format set to RGB888 */
ystride = width * bpp + 1;
data = (ystride << 16) | (mipi->vc << 8) | DTYPE_DCS_LWRITE;
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM0_CTRL,
data);
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM1_CTRL,
data);
data = height << 16 | width;
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM1_TOTAL,
data);
MIPI_OUTP(ctrl_base + DSI_COMMAND_MODE_MDP_STREAM0_TOTAL,
data);
}
msm_dsi_sw_reset();
msm_dsi_host_init(mipi);
if (mipi->force_clk_lane_hs) {
u32 tmp;
tmp = MIPI_INP(ctrl_base + DSI_LANE_CTRL);
tmp |= (1<<28);
MIPI_OUTP(ctrl_base + DSI_LANE_CTRL, tmp);
wmb();
}
msm_dsi_op_mode_config(mipi->mode, pdata);
return ret;
}
static int msm_dsi_off(struct mdss_panel_data *pdata)
{
int ret = 0;
pr_debug("msm_dsi_off\n");
msm_dsi_controller_cfg(0);
msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, 0, 0, 0);
msm_dsi_clk_disable();
msm_dsi_unprepare_clocks();
msm_dsi_phy_off(dsi_host_private->dsi_base);
msm_dsi_ahb_ctrl(0);
ret = msm_dsi_regulator_disable();
if (ret) {
pr_err("%s: Panel power off failed\n", __func__);
return ret;
}
return ret;
}
static int __devinit msm_dsi_probe(struct platform_device *pdev)
{
struct dsi_interface intf;
int rc = 0;
pr_debug("%s\n", __func__);
rc = msm_dsi_init();
if (rc)
return rc;
if (pdev->dev.of_node) {
struct resource *mdss_dsi_mres;
pdev->id = 0;
mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mdss_dsi_mres) {
pr_err("%s:%d unable to get the MDSS reg resources",
__func__, __LINE__);
return -ENOMEM;
} else {
dsi_host_private->dsi_base = ioremap(
mdss_dsi_mres->start,
resource_size(mdss_dsi_mres));
if (!dsi_host_private->dsi_base) {
pr_err("%s:%d unable to remap dsi resources",
__func__, __LINE__);
return -ENOMEM;
}
}
mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!mdss_dsi_mres || mdss_dsi_mres->start == 0) {
pr_err("%s:%d unable to get the MDSS irq resources",
__func__, __LINE__);
rc = -ENODEV;
goto dsi_probe_error;
} else {
rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start);
if (rc) {
dev_err(&pdev->dev,
"%s: failed to init irq, rc=%d\n",
__func__, rc);
goto dsi_probe_error;
}
}
rc = msm_dsi_io_init(pdev);
if (rc) {
dev_err(&pdev->dev,
"%s: failed to init DSI IO, rc=%d\n",
__func__, rc);
goto dsi_probe_error;
}
rc = of_platform_populate(pdev->dev.of_node,
NULL, NULL, &pdev->dev);
if (rc) {
dev_err(&pdev->dev,
"%s: failed to add child nodes, rc=%d\n",
__func__, rc);
goto dsi_probe_error;
}
}
dsi_host_private->dis_dev = pdev->dev;
intf.on = msm_dsi_on;
intf.off = msm_dsi_off;
intf.op_mode_config = msm_dsi_op_mode_config;
intf.tx = msm_dsi_cmds_tx;
intf.rx = msm_dsi_cmds_rx;
intf.index = 0;
intf.private = NULL;
dsi_register_interface(&intf);
pr_debug("%s success\n", __func__);
return 0;
dsi_probe_error:
if (dsi_host_private->dsi_base) {
iounmap(dsi_host_private->dsi_base);
dsi_host_private->dsi_base = NULL;
}
msm_dsi_io_deinit();
msm_dsi_deinit();
return rc;
}
static int __devexit msm_dsi_remove(struct platform_device *pdev)
{
msm_dsi_disable_irq();
msm_dsi_io_deinit();
iounmap(dsi_host_private->dsi_base);
dsi_host_private->dsi_base = NULL;
msm_dsi_deinit();
return 0;
}
static const struct of_device_id msm_dsi_v2_dt_match[] = {
{.compatible = "qcom,msm-dsi-v2"},
{}
};
MODULE_DEVICE_TABLE(of, msm_dsi_v2_dt_match);
static struct platform_driver msm_dsi_v2_driver = {
.probe = msm_dsi_probe,
.remove = __devexit_p(msm_dsi_remove),
.shutdown = NULL,
.driver = {
.name = "msm_dsi_v2",
.of_match_table = msm_dsi_v2_dt_match,
},
};
static int msm_dsi_v2_register_driver(void)
{
return platform_driver_register(&msm_dsi_v2_driver);
}
static int __init msm_dsi_v2_driver_init(void)
{
int ret;
ret = msm_dsi_v2_register_driver();
if (ret) {
pr_err("msm_dsi_v2_register_driver() failed!\n");
return ret;
}
return ret;
}
module_init(msm_dsi_v2_driver_init);
static void __exit msm_dsi_v2_driver_cleanup(void)
{
platform_driver_unregister(&msm_dsi_v2_driver);
}
module_exit(msm_dsi_v2_driver_cleanup);