1889 lines
46 KiB
C
1889 lines
46 KiB
C
/* Copyright (c) 2012-2015, 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"
|
|
#include "mdss_debug.h"
|
|
#include "mdp3.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
|
|
#define VSYNC_PERIOD 17
|
|
#define DSI_MAX_PKT_SIZE 10
|
|
#define DSI_SHORT_PKT_DATA_SIZE 2
|
|
#define DSI_MAX_BYTES_TO_READ 16
|
|
|
|
struct dsi_host_v2_private {
|
|
unsigned char *dsi_base;
|
|
size_t dsi_reg_size;
|
|
struct device dis_dev;
|
|
int clk_count;
|
|
int dsi_on;
|
|
|
|
void (*debug_enable_clk)(int on);
|
|
};
|
|
|
|
static struct dsi_host_v2_private *dsi_host_private;
|
|
static int msm_dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
/* Writing of an extra 0 needed to clear error bits */
|
|
MIPI_OUTP(ctrl_base + DSI_ACK_ERR_STATUS, 0);
|
|
pr_err("%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_err("%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_err("%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_err("%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_err("%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);
|
|
}
|
|
|
|
static void msm_dsi_set_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
|
|
{
|
|
u32 intr_ctrl;
|
|
intr_ctrl = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
|
|
intr_ctrl |= mask;
|
|
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, intr_ctrl);
|
|
}
|
|
|
|
static void msm_dsi_clear_irq_mask(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
|
|
{
|
|
u32 intr_ctrl;
|
|
intr_ctrl = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
|
|
intr_ctrl &= ~mask;
|
|
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, intr_ctrl);
|
|
}
|
|
|
|
static void msm_dsi_set_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ctrl->irq_lock, flags);
|
|
if (ctrl->dsi_irq_mask & mask) {
|
|
spin_unlock_irqrestore(&ctrl->irq_lock, flags);
|
|
return;
|
|
}
|
|
if (ctrl->dsi_irq_mask == 0) {
|
|
ctrl->mdss_util->enable_irq(ctrl->dsi_hw);
|
|
pr_debug("%s: IRQ Enable, mask=%x term=%x\n", __func__,
|
|
(int)ctrl->dsi_irq_mask, (int)mask);
|
|
}
|
|
|
|
msm_dsi_set_irq_mask(ctrl, mask);
|
|
ctrl->dsi_irq_mask |= mask;
|
|
spin_unlock_irqrestore(&ctrl->irq_lock, flags);
|
|
}
|
|
|
|
static void msm_dsi_clear_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 mask)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ctrl->irq_lock, flags);
|
|
if (!(ctrl->dsi_irq_mask & mask)) {
|
|
spin_unlock_irqrestore(&ctrl->irq_lock, flags);
|
|
return;
|
|
}
|
|
ctrl->dsi_irq_mask &= ~mask;
|
|
if (ctrl->dsi_irq_mask == 0) {
|
|
ctrl->mdss_util->disable_irq(ctrl->dsi_hw);
|
|
pr_debug("%s: IRQ Disable, mask=%x term=%x\n", __func__,
|
|
(int)ctrl->dsi_irq_mask, (int)mask);
|
|
}
|
|
msm_dsi_clear_irq_mask(ctrl, mask);
|
|
spin_unlock_irqrestore(&ctrl->irq_lock, flags);
|
|
}
|
|
|
|
irqreturn_t msm_dsi_isr_handler(int irq, void *ptr)
|
|
{
|
|
u32 isr;
|
|
|
|
struct mdss_dsi_ctrl_pdata *ctrl =
|
|
(struct mdss_dsi_ctrl_pdata *)ptr;
|
|
|
|
spin_lock(&ctrl->mdp_lock);
|
|
|
|
if (ctrl->dsi_irq_mask == 0) {
|
|
spin_unlock(&ctrl->mdp_lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
|
|
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
|
|
|
|
pr_debug("%s: isr=%x", __func__, isr);
|
|
|
|
if (isr & DSI_INTR_ERROR) {
|
|
pr_err("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR);
|
|
msm_dsi_error(dsi_host_private->dsi_base);
|
|
}
|
|
|
|
if (isr & DSI_INTR_VIDEO_DONE)
|
|
complete(&ctrl->video_comp);
|
|
|
|
if (isr & DSI_INTR_CMD_DMA_DONE)
|
|
complete(&ctrl->dma_comp);
|
|
|
|
if (isr & DSI_INTR_BTA_DONE)
|
|
complete(&ctrl->bta_comp);
|
|
|
|
if (isr & DSI_INTR_CMD_MDP_DONE)
|
|
complete(&ctrl->mdp_comp);
|
|
|
|
spin_unlock(&ctrl->mdp_lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int msm_dsi_irq_init(struct device *dev, int irq_no,
|
|
struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
int ret;
|
|
u32 isr;
|
|
struct mdss_hw *dsi_hw;
|
|
|
|
msm_dsi_ahb_ctrl(1);
|
|
isr = MIPI_INP(dsi_host_private->dsi_base + DSI_INT_CTRL);
|
|
isr &= ~DSI_INTR_ALL_MASK;
|
|
MIPI_OUTP(dsi_host_private->dsi_base + DSI_INT_CTRL, isr);
|
|
msm_dsi_ahb_ctrl(0);
|
|
|
|
ret = devm_request_irq(dev, irq_no, msm_dsi_isr_handler,
|
|
IRQF_DISABLED, "DSI", ctrl);
|
|
if (ret) {
|
|
pr_err("msm_dsi_irq_init request_irq() failed!\n");
|
|
return ret;
|
|
}
|
|
|
|
dsi_hw = kzalloc(sizeof(struct mdss_hw), GFP_KERNEL);
|
|
if (!dsi_hw) {
|
|
pr_err("no mem to save hw info: kzalloc fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
ctrl->dsi_hw = dsi_hw;
|
|
|
|
dsi_hw->irq_info = kzalloc(sizeof(struct irq_info), GFP_KERNEL);
|
|
if (!dsi_hw->irq_info) {
|
|
kfree(dsi_hw);
|
|
pr_err("no mem to save irq info: kzalloc fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dsi_hw->hw_ndx = MDSS_HW_DSI0;
|
|
dsi_hw->irq_info->irq = irq_no;
|
|
dsi_hw->irq_info->irq_mask = 0;
|
|
dsi_hw->irq_info->irq_ena = false;
|
|
dsi_hw->irq_info->irq_buzy = false;
|
|
|
|
ctrl->mdss_util->register_irq(ctrl->dsi_hw);
|
|
ctrl->mdss_util->disable_irq(ctrl->dsi_hw);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void msm_dsi_get_cmd_engine(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
u32 dsi_ctrl;
|
|
|
|
if (ctrl->panel_mode == DSI_VIDEO_MODE) {
|
|
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
|
|
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl | 0x04);
|
|
}
|
|
}
|
|
|
|
static void msm_dsi_release_cmd_engine(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
u32 dsi_ctrl;
|
|
if (ctrl->panel_mode == DSI_VIDEO_MODE) {
|
|
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL);
|
|
dsi_ctrl &= ~0x04;
|
|
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
|
|
}
|
|
}
|
|
|
|
static int msm_dsi_wait4mdp_done(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
int rc;
|
|
unsigned long flag;
|
|
|
|
spin_lock_irqsave(&ctrl->mdp_lock, flag);
|
|
reinit_completion(&ctrl->mdp_comp);
|
|
msm_dsi_set_irq(ctrl, DSI_INTR_CMD_MDP_DONE_MASK);
|
|
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
|
|
|
|
rc = wait_for_completion_timeout(&ctrl->mdp_comp,
|
|
msecs_to_jiffies(VSYNC_PERIOD * 4));
|
|
|
|
if (rc == 0) {
|
|
pr_err("DSI wait 4 mdp done time out\n");
|
|
rc = -ETIME;
|
|
} else if (!IS_ERR_VALUE(rc)) {
|
|
rc = 0;
|
|
}
|
|
|
|
msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_MDP_DONE_MASK);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void msm_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
int rc;
|
|
u32 dsi_status;
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
|
|
if (ctrl->panel_mode == DSI_VIDEO_MODE)
|
|
return;
|
|
|
|
dsi_status = MIPI_INP(ctrl_base + DSI_STATUS);
|
|
if (dsi_status & 0x04) {
|
|
pr_debug("dsi command engine is busy\n");
|
|
rc = msm_dsi_wait4mdp_done(ctrl);
|
|
if (rc)
|
|
pr_err("Timed out waiting for mdp done");
|
|
}
|
|
}
|
|
|
|
static int msm_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
int rc;
|
|
unsigned long flag;
|
|
|
|
spin_lock_irqsave(&ctrl->mdp_lock, flag);
|
|
reinit_completion(&ctrl->video_comp);
|
|
msm_dsi_set_irq(ctrl, DSI_INTR_VIDEO_DONE_MASK);
|
|
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
|
|
|
|
rc = wait_for_completion_timeout(&ctrl->video_comp,
|
|
msecs_to_jiffies(VSYNC_PERIOD * 4));
|
|
|
|
if (rc == 0) {
|
|
pr_err("DSI wait 4 video done time out\n");
|
|
rc = -ETIME;
|
|
} else if (!IS_ERR_VALUE(rc)) {
|
|
rc = 0;
|
|
}
|
|
|
|
msm_dsi_clear_irq(ctrl, DSI_INTR_VIDEO_DONE_MASK);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int msm_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
int rc = 0;
|
|
u32 dsi_status;
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
|
|
if (ctrl->panel_mode == DSI_CMD_MODE)
|
|
return rc;
|
|
|
|
dsi_status = MIPI_INP(ctrl_base + DSI_STATUS);
|
|
if (dsi_status & 0x08) {
|
|
pr_debug("dsi command in video mode wait for active region\n");
|
|
rc = msm_dsi_wait4video_done(ctrl);
|
|
/* delay 4-5 ms to skip BLLP */
|
|
if (!rc)
|
|
usleep_range(4000, 5000);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void msm_dsi_host_init(struct mdss_panel_data *pdata)
|
|
{
|
|
u32 dsi_ctrl, data;
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
|
|
struct mipi_panel_info *pinfo;
|
|
|
|
pr_debug("msm_dsi_host_init\n");
|
|
|
|
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
|
|
panel_data);
|
|
pinfo = &pdata->panel_info.mipi;
|
|
|
|
|
|
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 */
|
|
|
|
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, ctrl_pdata->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);
|
|
|
|
/* 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 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);
|
|
pr_err("%s: Doing sw reset\n", __func__);
|
|
msm_dsi_sw_reset();
|
|
}
|
|
|
|
/* 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;
|
|
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 (dsi_ctrl & DSI_VIDEO_MODE_EN)
|
|
dsi_ctrl &= ~(DSI_CMD_MODE_EN|DSI_EN);
|
|
else
|
|
dsi_ctrl &= ~(DSI_CMD_MODE_EN|DSI_VIDEO_MODE_EN|DSI_EN);
|
|
|
|
if (mode == DSI_VIDEO_MODE) {
|
|
dsi_ctrl |= (DSI_VIDEO_MODE_EN|DSI_EN);
|
|
} else {
|
|
dsi_ctrl |= (DSI_CMD_MODE_EN|DSI_EN);
|
|
/* For Video mode panel, keep Video and Cmd mode ON */
|
|
if (pdata->panel_info.type == MIPI_VIDEO_PANEL)
|
|
dsi_ctrl |= DSI_VIDEO_MODE_EN;
|
|
}
|
|
|
|
pr_debug("%s: dsi_ctrl=%x\n", __func__, dsi_ctrl);
|
|
|
|
MIPI_OUTP(ctrl_base + DSI_CTRL, dsi_ctrl);
|
|
wmb();
|
|
}
|
|
|
|
int msm_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dsi_buf *tp)
|
|
{
|
|
int len, rc;
|
|
unsigned long size, addr;
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
unsigned long flag;
|
|
|
|
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;
|
|
|
|
msm_dsi_get_cmd_engine(ctrl);
|
|
|
|
spin_lock_irqsave(&ctrl->mdp_lock, flag);
|
|
reinit_completion(&ctrl->dma_comp);
|
|
msm_dsi_set_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);
|
|
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
|
|
|
|
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(&ctrl->dma_comp,
|
|
msecs_to_jiffies(DSI_DMA_CMD_TIMEOUT_MS));
|
|
if (rc == 0) {
|
|
pr_err("DSI command transaction time out\n");
|
|
rc = -ETIME;
|
|
} else if (!IS_ERR_VALUE(rc)) {
|
|
rc = 0;
|
|
}
|
|
|
|
dma_unmap_single(&dsi_host_private->dis_dev, tp->dmap, size,
|
|
DMA_TO_DEVICE);
|
|
tp->dmap = 0;
|
|
|
|
msm_dsi_clear_irq(ctrl, DSI_INTR_CMD_DMA_DONE_MASK);
|
|
|
|
msm_dsi_release_cmd_engine(ctrl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int msm_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
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 rlen;
|
|
}
|
|
|
|
static int msm_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dsi_cmd_desc *cmds, int cnt)
|
|
{
|
|
struct dsi_buf *tp;
|
|
struct dsi_cmd_desc *cm;
|
|
struct dsi_ctrl_hdr *dchdr;
|
|
int len;
|
|
int rc = 0;
|
|
|
|
|
|
tp = &ctrl->tx_buf;
|
|
mdss_dsi_buf_init(tp);
|
|
cm = cmds;
|
|
len = 0;
|
|
while (cnt--) {
|
|
dchdr = &cm->dchdr;
|
|
mdss_dsi_buf_reserve(tp, len);
|
|
len = mdss_dsi_cmd_dma_add(tp, cm);
|
|
if (!len) {
|
|
pr_err("%s: failed to add cmd = 0x%x\n",
|
|
__func__, cm->payload[0]);
|
|
rc = -EINVAL;
|
|
goto dsi_cmds_tx_err;
|
|
}
|
|
|
|
if (dchdr->last) {
|
|
tp->data = tp->start; /* begin of buf */
|
|
rc = msm_dsi_wait4video_eng_busy(ctrl);
|
|
if (rc) {
|
|
pr_err("%s: wait4video_eng failed\n", __func__);
|
|
goto dsi_cmds_tx_err;
|
|
|
|
}
|
|
|
|
rc = msm_dsi_cmd_dma_tx(ctrl, tp);
|
|
if (IS_ERR_VALUE(len)) {
|
|
pr_err("%s: failed to call cmd_dma_tx for cmd = 0x%x\n",
|
|
__func__, cmds->payload[0]);
|
|
goto dsi_cmds_tx_err;
|
|
}
|
|
|
|
if (dchdr->wait)
|
|
usleep_range(dchdr->wait * 1000, dchdr->wait * 1000);
|
|
|
|
mdss_dsi_buf_init(tp);
|
|
len = 0;
|
|
}
|
|
cm++;
|
|
}
|
|
|
|
dsi_cmds_tx_err:
|
|
return rc;
|
|
}
|
|
|
|
static int msm_dsi_parse_rx_response(struct dsi_buf *rp)
|
|
{
|
|
int rc = 0;
|
|
unsigned char cmd;
|
|
|
|
cmd = rp->data[0];
|
|
switch (cmd) {
|
|
case DTYPE_ACK_ERR_RESP:
|
|
pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__);
|
|
rc = -EINVAL;
|
|
break;
|
|
case DTYPE_GEN_READ1_RESP:
|
|
case DTYPE_DCS_READ1_RESP:
|
|
mdss_dsi_short_read1_resp(rp);
|
|
break;
|
|
case DTYPE_GEN_READ2_RESP:
|
|
case DTYPE_DCS_READ2_RESP:
|
|
mdss_dsi_short_read2_resp(rp);
|
|
break;
|
|
case DTYPE_GEN_LREAD_RESP:
|
|
case DTYPE_DCS_LREAD_RESP:
|
|
mdss_dsi_long_read_resp(rp);
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
pr_warn("%s: Unknown cmd received\n", __func__);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* MIPI_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,
|
|
};
|
|
|
|
static int msm_dsi_set_max_packet_size(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
int size)
|
|
{
|
|
struct dsi_buf *tp;
|
|
int rc;
|
|
|
|
tp = &ctrl->tx_buf;
|
|
mdss_dsi_buf_init(tp);
|
|
max_pktsize[0] = size;
|
|
|
|
rc = mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd);
|
|
if (!rc) {
|
|
pr_err("%s: failed to add max_pkt_size\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = msm_dsi_wait4video_eng_busy(ctrl);
|
|
if (rc) {
|
|
pr_err("%s: failed to wait4video_eng\n", __func__);
|
|
return rc;
|
|
}
|
|
|
|
rc = msm_dsi_cmd_dma_tx(ctrl, tp);
|
|
if (IS_ERR_VALUE(rc)) {
|
|
pr_err("%s: failed to tx max_pkt_size\n", __func__);
|
|
return rc;
|
|
}
|
|
pr_debug("%s: max_pkt_size=%d sent\n", __func__, size);
|
|
return rc;
|
|
}
|
|
|
|
/* read data length is less than or equal to 10 bytes*/
|
|
static int msm_dsi_cmds_rx_1(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dsi_cmd_desc *cmds, int rlen)
|
|
{
|
|
int rc;
|
|
struct dsi_buf *tp, *rp;
|
|
|
|
tp = &ctrl->tx_buf;
|
|
rp = &ctrl->rx_buf;
|
|
mdss_dsi_buf_init(rp);
|
|
mdss_dsi_buf_init(tp);
|
|
|
|
rc = mdss_dsi_cmd_dma_add(tp, cmds);
|
|
if (!rc) {
|
|
pr_err("%s: dsi_cmd_dma_add failed\n", __func__);
|
|
rc = -EINVAL;
|
|
goto dsi_cmds_rx_1_error;
|
|
}
|
|
|
|
rc = msm_dsi_wait4video_eng_busy(ctrl);
|
|
if (rc) {
|
|
pr_err("%s: wait4video_eng failed\n", __func__);
|
|
goto dsi_cmds_rx_1_error;
|
|
}
|
|
|
|
rc = msm_dsi_cmd_dma_tx(ctrl, tp);
|
|
if (IS_ERR_VALUE(rc)) {
|
|
pr_err("%s: msm_dsi_cmd_dma_tx failed\n", __func__);
|
|
goto dsi_cmds_rx_1_error;
|
|
}
|
|
|
|
if (rlen <= DSI_SHORT_PKT_DATA_SIZE) {
|
|
msm_dsi_cmd_dma_rx(ctrl, rp, rlen);
|
|
} else {
|
|
msm_dsi_cmd_dma_rx(ctrl, rp, rlen + DSI_HOST_HDR_SIZE);
|
|
rp->len = rlen + DSI_HOST_HDR_SIZE;
|
|
}
|
|
rc = msm_dsi_parse_rx_response(rp);
|
|
|
|
dsi_cmds_rx_1_error:
|
|
if (rc)
|
|
rp->len = 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* read data length is more than 10 bytes, which requires multiple DSI read*/
|
|
static int msm_dsi_cmds_rx_2(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dsi_cmd_desc *cmds, int rlen)
|
|
{
|
|
int rc;
|
|
struct dsi_buf *tp, *rp;
|
|
int pkt_size, data_bytes, total;
|
|
|
|
tp = &ctrl->tx_buf;
|
|
rp = &ctrl->rx_buf;
|
|
mdss_dsi_buf_init(rp);
|
|
pkt_size = DSI_MAX_PKT_SIZE;
|
|
data_bytes = MDSS_DSI_LEN;
|
|
total = 0;
|
|
|
|
while (true) {
|
|
rc = msm_dsi_set_max_packet_size(ctrl, pkt_size);
|
|
if (rc)
|
|
break;
|
|
|
|
mdss_dsi_buf_init(tp);
|
|
rc = mdss_dsi_cmd_dma_add(tp, cmds);
|
|
if (!rc) {
|
|
pr_err("%s: dsi_cmd_dma_add failed\n", __func__);
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
rc = msm_dsi_wait4video_eng_busy(ctrl);
|
|
if (rc) {
|
|
pr_err("%s: wait4video_eng failed\n", __func__);
|
|
break;
|
|
}
|
|
|
|
rc = msm_dsi_cmd_dma_tx(ctrl, tp);
|
|
if (IS_ERR_VALUE(rc)) {
|
|
pr_err("%s: msm_dsi_cmd_dma_tx failed\n", __func__);
|
|
break;
|
|
}
|
|
|
|
msm_dsi_cmd_dma_rx(ctrl, rp, DSI_MAX_BYTES_TO_READ);
|
|
|
|
rp->data += DSI_MAX_BYTES_TO_READ - DSI_HOST_HDR_SIZE;
|
|
total += data_bytes;
|
|
if (total >= rlen)
|
|
break;
|
|
|
|
data_bytes = DSI_MAX_BYTES_TO_READ - DSI_HOST_HDR_SIZE;
|
|
pkt_size += data_bytes;
|
|
}
|
|
|
|
if (!rc) {
|
|
rp->data = rp->start;
|
|
rp->len = rlen + DSI_HOST_HDR_SIZE;
|
|
rc = msm_dsi_parse_rx_response(rp);
|
|
}
|
|
|
|
if (rc)
|
|
rp->len = 0;
|
|
|
|
return rc;
|
|
}
|
|
|
|
int msm_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dsi_cmd_desc *cmds, int rlen)
|
|
{
|
|
int rc;
|
|
if (rlen <= DSI_MAX_PKT_SIZE)
|
|
rc = msm_dsi_cmds_rx_1(ctrl, cmds, rlen);
|
|
else
|
|
rc = msm_dsi_cmds_rx_2(ctrl, cmds, rlen);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void msm_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dcs_cmd_req *req)
|
|
{
|
|
int ret;
|
|
|
|
ret = msm_dsi_cmds_tx(ctrl, req->cmds, req->cmds_cnt);
|
|
|
|
if (req->cb)
|
|
req->cb(ret);
|
|
}
|
|
|
|
void msm_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl,
|
|
struct dcs_cmd_req *req)
|
|
{
|
|
struct dsi_buf *rp;
|
|
int len = 0;
|
|
|
|
if (req->rbuf) {
|
|
rp = &ctrl->rx_buf;
|
|
len = msm_dsi_cmds_rx(ctrl, req->cmds, req->rlen);
|
|
memcpy(req->rbuf, rp->data, rp->len);
|
|
} else {
|
|
pr_err("%s: No rx buffer provided\n", __func__);
|
|
}
|
|
|
|
if (req->cb)
|
|
req->cb(len);
|
|
}
|
|
int msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
|
|
{
|
|
struct dcs_cmd_req *req;
|
|
int dsi_on;
|
|
int ret = -EINVAL;
|
|
|
|
mutex_lock(&ctrl->mutex);
|
|
dsi_on = dsi_host_private->dsi_on;
|
|
mutex_unlock(&ctrl->mutex);
|
|
if (!dsi_on) {
|
|
pr_err("try to send DSI commands while dsi is off\n");
|
|
return ret;
|
|
}
|
|
|
|
if (from_mdp) /* from mdp kickoff */
|
|
mutex_lock(&ctrl->cmd_mutex);
|
|
req = mdss_dsi_cmdlist_get(ctrl);
|
|
|
|
if (!req) {
|
|
mutex_unlock(&ctrl->cmd_mutex);
|
|
return ret;
|
|
}
|
|
/*
|
|
* mdss interrupt is generated in mdp core clock domain
|
|
* mdp clock need to be enabled to receive dsi interrupt
|
|
* also, axi bus bandwidth need since dsi controller will
|
|
* fetch dcs commands from axi bus
|
|
*/
|
|
mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P);
|
|
msm_dsi_clk_ctrl(&ctrl->panel_data, 1);
|
|
|
|
if (0 == (req->flags & CMD_REQ_LP_MODE))
|
|
dsi_set_tx_power_mode(0);
|
|
|
|
if (req->flags & CMD_REQ_RX)
|
|
msm_dsi_cmdlist_rx(ctrl, req);
|
|
else
|
|
msm_dsi_cmdlist_tx(ctrl, req);
|
|
|
|
if (0 == (req->flags & CMD_REQ_LP_MODE))
|
|
dsi_set_tx_power_mode(1);
|
|
|
|
msm_dsi_clk_ctrl(&ctrl->panel_data, 0);
|
|
mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
|
|
|
|
if (from_mdp) /* from mdp kickoff */
|
|
mutex_unlock(&ctrl->cmd_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static int msm_dsi_cal_clk_rate(struct mdss_panel_data *pdata,
|
|
u64 *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;
|
|
u64 clk_rate;
|
|
|
|
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;
|
|
do_div(*bitclk_rate, lanes);
|
|
clk_rate = *bitclk_rate;
|
|
|
|
do_div(clk_rate, 8U);
|
|
*byteclk_rate = (u32) clk_rate;
|
|
*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, i;
|
|
u64 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;
|
|
u64 bitclk_rate = 0
|
|
u32 byteclk_rate = 0, pclk_rate = 0, dsiclk_rate = 0;
|
|
unsigned char *ctrl_base = dsi_host_private->dsi_base;
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
|
|
|
|
pr_debug("msm_dsi_on\n");
|
|
|
|
pinfo = &pdata->panel_info;
|
|
|
|
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
|
|
panel_data);
|
|
|
|
mutex_lock(&ctrl_pdata->mutex);
|
|
|
|
|
|
if (!pdata->panel_info.dynamic_switch_pending) {
|
|
for (i = 0; !ret && (i < DSI_MAX_PM); i++) {
|
|
ret = msm_dss_enable_vreg(
|
|
ctrl_pdata->power_data[i].vreg_config,
|
|
ctrl_pdata->power_data[i].num_vreg, 1);
|
|
if (ret) {
|
|
pr_err("%s: failed to enable vregs for %s\n",
|
|
__func__, __mdss_dsi_pm_name(i));
|
|
goto error_vreg;
|
|
}
|
|
}
|
|
}
|
|
|
|
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(pdata);
|
|
|
|
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);
|
|
|
|
msm_dsi_set_irq(ctrl_pdata, DSI_INTR_ERROR_MASK);
|
|
dsi_host_private->clk_count = 1;
|
|
dsi_host_private->dsi_on = 1;
|
|
|
|
error_vreg:
|
|
if (ret) {
|
|
for (; i >= 0; i--)
|
|
msm_dss_enable_vreg(
|
|
ctrl_pdata->power_data[i].vreg_config,
|
|
ctrl_pdata->power_data[i].num_vreg, 0);
|
|
}
|
|
|
|
mutex_unlock(&ctrl_pdata->mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int msm_dsi_off(struct mdss_panel_data *pdata)
|
|
{
|
|
int ret = 0, i;
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
|
|
|
|
if (pdata == NULL) {
|
|
pr_err("%s: Invalid input data\n", __func__);
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
|
|
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
|
|
panel_data);
|
|
|
|
pr_debug("msm_dsi_off\n");
|
|
mutex_lock(&ctrl_pdata->mutex);
|
|
msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask);
|
|
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);
|
|
|
|
if (!pdata->panel_info.dynamic_switch_pending) {
|
|
for (i = DSI_MAX_PM - 1; i >= 0; i--) {
|
|
ret = msm_dss_enable_vreg(
|
|
ctrl_pdata->power_data[i].vreg_config,
|
|
ctrl_pdata->power_data[i].num_vreg, 0);
|
|
if (ret)
|
|
pr_err("%s: failed to disable vregs for %s\n",
|
|
__func__, __mdss_dsi_pm_name(i));
|
|
}
|
|
}
|
|
dsi_host_private->clk_count = 0;
|
|
dsi_host_private->dsi_on = 0;
|
|
|
|
mutex_unlock(&ctrl_pdata->mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int msm_dsi_cont_on(struct mdss_panel_data *pdata)
|
|
{
|
|
struct mdss_panel_info *pinfo;
|
|
int ret = 0, i;
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
|
|
|
|
if (pdata == NULL) {
|
|
pr_err("%s: Invalid input data\n", __func__);
|
|
ret = -EINVAL;
|
|
return ret;
|
|
}
|
|
|
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
|
|
panel_data);
|
|
|
|
pinfo = &pdata->panel_info;
|
|
mutex_lock(&ctrl_pdata->mutex);
|
|
for (i = 0; !ret && (i < DSI_MAX_PM); i++) {
|
|
ret = msm_dss_enable_vreg(
|
|
ctrl_pdata->power_data[i].vreg_config,
|
|
ctrl_pdata->power_data[i].num_vreg, 1);
|
|
if (ret) {
|
|
pr_err("%s: failed to enable vregs for %s\n",
|
|
__func__, __mdss_dsi_pm_name(i));
|
|
goto error_vreg;
|
|
}
|
|
}
|
|
pinfo->panel_power_state = MDSS_PANEL_POWER_ON;
|
|
ret = mdss_dsi_panel_reset(pdata, 1);
|
|
if (ret) {
|
|
pr_err("%s: Panel reset failed\n", __func__);
|
|
mutex_unlock(&ctrl_pdata->mutex);
|
|
return ret;
|
|
}
|
|
|
|
msm_dsi_ahb_ctrl(1);
|
|
msm_dsi_prepare_clocks();
|
|
msm_dsi_clk_enable();
|
|
msm_dsi_set_irq(ctrl_pdata, DSI_INTR_ERROR_MASK);
|
|
dsi_host_private->clk_count = 1;
|
|
dsi_host_private->dsi_on = 1;
|
|
|
|
error_vreg:
|
|
if (ret) {
|
|
for (; i >= 0; i--)
|
|
msm_dss_enable_vreg(
|
|
ctrl_pdata->power_data[i].vreg_config,
|
|
ctrl_pdata->power_data[i].num_vreg, 0);
|
|
}
|
|
|
|
mutex_unlock(&ctrl_pdata->mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int msm_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
struct dcs_cmd_req cmdreq;
|
|
|
|
memset(&cmdreq, 0, sizeof(cmdreq));
|
|
cmdreq.cmds = ctrl->status_cmds.cmds;
|
|
cmdreq.cmds_cnt = ctrl->status_cmds.cmd_cnt;
|
|
cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_RX;
|
|
cmdreq.rlen = 1;
|
|
cmdreq.cb = NULL;
|
|
cmdreq.rbuf = ctrl->status_buf.data;
|
|
|
|
return mdss_dsi_cmdlist_put(ctrl, &cmdreq);
|
|
}
|
|
|
|
|
|
/**
|
|
* msm_dsi_reg_status_check() - Check dsi panel status through reg read
|
|
* @ctrl_pdata: pointer to the dsi controller structure
|
|
*
|
|
* This function can be used to check the panel status through reading the
|
|
* status register from the panel.
|
|
*
|
|
* Return: positive value if the panel is in good state, negative value or
|
|
* zero otherwise.
|
|
*/
|
|
int msm_dsi_reg_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (ctrl_pdata == NULL) {
|
|
pr_err("%s: Invalid input data\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
pr_debug("%s: Checking Register status\n", __func__);
|
|
|
|
msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 1);
|
|
|
|
if (ctrl_pdata->status_cmds.link_state == DSI_HS_MODE)
|
|
dsi_set_tx_power_mode(0);
|
|
|
|
ret = msm_dsi_read_status(ctrl_pdata);
|
|
|
|
if (ctrl_pdata->status_cmds.link_state == DSI_HS_MODE)
|
|
dsi_set_tx_power_mode(1);
|
|
|
|
if (ret == 0) {
|
|
if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf,
|
|
ctrl_pdata->status_value, 0)) {
|
|
pr_err("%s: Read back value from panel is incorrect\n",
|
|
__func__);
|
|
ret = -EINVAL;
|
|
} else {
|
|
ret = 1;
|
|
}
|
|
} else {
|
|
pr_err("%s: Read status register returned error\n", __func__);
|
|
}
|
|
|
|
msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 0);
|
|
pr_debug("%s: Read register done with ret: %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* msm_dsi_bta_status_check() - Check dsi panel status through bta check
|
|
* @ctrl_pdata: pointer to the dsi controller structure
|
|
*
|
|
* This function can be used to check status of the panel using bta check
|
|
* for the panel.
|
|
*
|
|
* Return: positive value if the panel is in good state, negative value or
|
|
* zero otherwise.
|
|
*/
|
|
static int msm_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (ctrl_pdata == NULL) {
|
|
pr_err("%s: Invalid input data\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
mutex_lock(&ctrl_pdata->cmd_mutex);
|
|
msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 1);
|
|
msm_dsi_cmd_mdp_busy(ctrl_pdata);
|
|
msm_dsi_set_irq(ctrl_pdata, DSI_INTR_BTA_DONE_MASK);
|
|
reinit_completion(&ctrl_pdata->bta_comp);
|
|
|
|
/* BTA trigger */
|
|
MIPI_OUTP(dsi_host_private->dsi_base + DSI_CMD_MODE_BTA_SW_TRIGGER,
|
|
0x01);
|
|
wmb();
|
|
ret = wait_for_completion_killable_timeout(&ctrl_pdata->bta_comp,
|
|
HZ/10);
|
|
msm_dsi_clear_irq(ctrl_pdata, DSI_INTR_BTA_DONE_MASK);
|
|
msm_dsi_clk_ctrl(&ctrl_pdata->panel_data, 0);
|
|
mutex_unlock(&ctrl_pdata->cmd_mutex);
|
|
|
|
if (ret <= 0)
|
|
pr_err("%s: DSI BTA error: %i\n", __func__, __LINE__);
|
|
|
|
pr_debug("%s: BTA done with ret: %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void msm_dsi_debug_enable_clock(int on)
|
|
{
|
|
if (dsi_host_private->debug_enable_clk)
|
|
dsi_host_private->debug_enable_clk(on);
|
|
|
|
if (on)
|
|
msm_dsi_ahb_ctrl(1);
|
|
else
|
|
msm_dsi_ahb_ctrl(0);
|
|
}
|
|
|
|
static int msm_dsi_debug_init(void)
|
|
{
|
|
int rc;
|
|
|
|
if (!mdss_res)
|
|
return 0;
|
|
|
|
dsi_host_private->debug_enable_clk =
|
|
mdss_res->debug_inf.debug_enable_clock;
|
|
|
|
mdss_res->debug_inf.debug_enable_clock = msm_dsi_debug_enable_clock;
|
|
|
|
|
|
rc = mdss_debug_register_base("dsi0",
|
|
dsi_host_private->dsi_base,
|
|
dsi_host_private->dsi_reg_size,
|
|
NULL);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int dsi_get_panel_cfg(char *panel_cfg)
|
|
{
|
|
int rc;
|
|
struct mdss_panel_cfg *pan_cfg = NULL;
|
|
|
|
if (!panel_cfg)
|
|
return MDSS_PANEL_INTF_INVALID;
|
|
|
|
pan_cfg = mdp3_panel_intf_type(MDSS_PANEL_INTF_DSI);
|
|
if (IS_ERR(pan_cfg)) {
|
|
panel_cfg[0] = 0;
|
|
return PTR_ERR(pan_cfg);
|
|
} else if (!pan_cfg) {
|
|
panel_cfg[0] = 0;
|
|
return 0;
|
|
}
|
|
|
|
pr_debug("%s:%d: cfg:[%s]\n", __func__, __LINE__,
|
|
pan_cfg->arg_cfg);
|
|
rc = strlcpy(panel_cfg, pan_cfg->arg_cfg,
|
|
MDSS_MAX_PANEL_LEN);
|
|
return rc;
|
|
}
|
|
|
|
static struct device_node *dsi_pref_prim_panel(
|
|
struct platform_device *pdev)
|
|
{
|
|
struct device_node *dsi_pan_node = NULL;
|
|
|
|
pr_debug("%s:%d: Select primary panel from dt\n",
|
|
__func__, __LINE__);
|
|
dsi_pan_node = of_parse_phandle(pdev->dev.of_node,
|
|
"qcom,dsi-pref-prim-pan", 0);
|
|
if (!dsi_pan_node)
|
|
pr_err("%s:can't find panel phandle\n", __func__);
|
|
|
|
return dsi_pan_node;
|
|
}
|
|
|
|
/**
|
|
* dsi_find_panel_of_node(): find device node of dsi panel
|
|
* @pdev: platform_device of the dsi ctrl node
|
|
* @panel_cfg: string containing intf specific config data
|
|
*
|
|
* Function finds the panel device node using the interface
|
|
* specific configuration data. This configuration data is
|
|
* could be derived from the result of bootloader's GCDB
|
|
* panel detection mechanism. If such config data doesn't
|
|
* exist then this panel returns the default panel configured
|
|
* in the device tree.
|
|
*
|
|
* returns pointer to panel node on success, NULL on error.
|
|
*/
|
|
static struct device_node *dsi_find_panel_of_node(
|
|
struct platform_device *pdev, char *panel_cfg)
|
|
{
|
|
int l;
|
|
char *panel_name;
|
|
struct device_node *dsi_pan_node = NULL, *mdss_node = NULL;
|
|
|
|
if (!panel_cfg)
|
|
return NULL;
|
|
|
|
l = strlen(panel_cfg);
|
|
if (!l) {
|
|
/* no panel cfg chg, parse dt */
|
|
pr_debug("%s:%d: no cmd line cfg present\n",
|
|
__func__, __LINE__);
|
|
dsi_pan_node = dsi_pref_prim_panel(pdev);
|
|
} else {
|
|
if (panel_cfg[0] != '0') {
|
|
pr_err("%s:%d:ctrl id=[%d] not supported\n",
|
|
__func__, __LINE__, panel_cfg[0]);
|
|
return NULL;
|
|
}
|
|
/*
|
|
* skip first two chars '<dsi_ctrl_id>' and
|
|
* ':' to get to the panel name
|
|
*/
|
|
panel_name = panel_cfg + 2;
|
|
pr_debug("%s:%d:%s:%s\n", __func__, __LINE__,
|
|
panel_cfg, panel_name);
|
|
|
|
mdss_node = of_parse_phandle(pdev->dev.of_node,
|
|
"qcom,mdss-mdp", 0);
|
|
|
|
if (!mdss_node) {
|
|
pr_err("%s: %d: mdss_node null\n",
|
|
__func__, __LINE__);
|
|
return NULL;
|
|
}
|
|
dsi_pan_node = of_find_node_by_name(mdss_node,
|
|
panel_name);
|
|
if (!dsi_pan_node) {
|
|
pr_err("%s: invalid pan node\n",
|
|
__func__);
|
|
dsi_pan_node = dsi_pref_prim_panel(pdev);
|
|
}
|
|
}
|
|
return dsi_pan_node;
|
|
}
|
|
|
|
static int msm_dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable)
|
|
{
|
|
u32 bitclk_rate = 0, byteclk_rate = 0, pclk_rate = 0, dsiclk_rate = 0;
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
|
|
|
|
pr_debug("%s:\n", __func__);
|
|
|
|
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
|
|
panel_data);
|
|
|
|
mutex_lock(&ctrl_pdata->mutex);
|
|
|
|
if (enable) {
|
|
dsi_host_private->clk_count++;
|
|
if (dsi_host_private->clk_count == 1) {
|
|
msm_dsi_ahb_ctrl(1);
|
|
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();
|
|
}
|
|
} else {
|
|
dsi_host_private->clk_count--;
|
|
if (dsi_host_private->clk_count == 0) {
|
|
msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask);
|
|
msm_dsi_clk_set_rate(DSI_ESC_CLK_RATE, 0, 0, 0);
|
|
msm_dsi_clk_disable();
|
|
msm_dsi_unprepare_clocks();
|
|
msm_dsi_ahb_ctrl(0);
|
|
}
|
|
}
|
|
mutex_unlock(&ctrl_pdata->mutex);
|
|
return 0;
|
|
}
|
|
|
|
void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
|
|
{
|
|
init_completion(&ctrl->dma_comp);
|
|
init_completion(&ctrl->mdp_comp);
|
|
init_completion(&ctrl->bta_comp);
|
|
init_completion(&ctrl->video_comp);
|
|
spin_lock_init(&ctrl->irq_lock);
|
|
spin_lock_init(&ctrl->mdp_lock);
|
|
mutex_init(&ctrl->mutex);
|
|
mutex_init(&ctrl->cmd_mutex);
|
|
complete(&ctrl->mdp_comp);
|
|
dsi_buf_alloc(&ctrl->tx_buf, SZ_4K);
|
|
dsi_buf_alloc(&ctrl->rx_buf, SZ_4K);
|
|
dsi_buf_alloc(&ctrl->status_buf, SZ_4K);
|
|
ctrl->cmdlist_commit = msm_dsi_cmdlist_commit;
|
|
ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode;
|
|
|
|
if (ctrl->status_mode == ESD_REG)
|
|
ctrl->check_status = msm_dsi_reg_status_check;
|
|
else if (ctrl->status_mode == ESD_BTA)
|
|
ctrl->check_status = msm_dsi_bta_status_check;
|
|
|
|
if (ctrl->status_mode == ESD_MAX) {
|
|
pr_err("%s: Using default BTA for ESD check\n", __func__);
|
|
ctrl->check_status = msm_dsi_bta_status_check;
|
|
}
|
|
}
|
|
|
|
static void msm_dsi_parse_lane_swap(struct device_node *np, char *dlane_swap)
|
|
{
|
|
const char *data;
|
|
|
|
*dlane_swap = DSI_LANE_MAP_0123;
|
|
data = of_get_property(np, "qcom,lane-map", NULL);
|
|
if (data) {
|
|
if (!strcmp(data, "lane_map_3012"))
|
|
*dlane_swap = DSI_LANE_MAP_3012;
|
|
else if (!strcmp(data, "lane_map_2301"))
|
|
*dlane_swap = DSI_LANE_MAP_2301;
|
|
else if (!strcmp(data, "lane_map_1230"))
|
|
*dlane_swap = DSI_LANE_MAP_1230;
|
|
else if (!strcmp(data, "lane_map_0321"))
|
|
*dlane_swap = DSI_LANE_MAP_0321;
|
|
else if (!strcmp(data, "lane_map_1032"))
|
|
*dlane_swap = DSI_LANE_MAP_1032;
|
|
else if (!strcmp(data, "lane_map_2103"))
|
|
*dlane_swap = DSI_LANE_MAP_2103;
|
|
else if (!strcmp(data, "lane_map_3210"))
|
|
*dlane_swap = DSI_LANE_MAP_3210;
|
|
}
|
|
}
|
|
|
|
static int msm_dsi_probe(struct platform_device *pdev)
|
|
{
|
|
struct dsi_interface intf;
|
|
char panel_cfg[MDSS_MAX_PANEL_LEN];
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
|
|
int rc = 0;
|
|
struct device_node *dsi_pan_node = NULL;
|
|
bool cmd_cfg_cont_splash = false;
|
|
struct resource *mdss_dsi_mres;
|
|
int i;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
rc = msm_dsi_init();
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (!pdev->dev.of_node) {
|
|
pr_err("%s: Device node is not accessible\n", __func__);
|
|
rc = -ENODEV;
|
|
goto error_no_mem;
|
|
}
|
|
pdev->id = 0;
|
|
|
|
ctrl_pdata = platform_get_drvdata(pdev);
|
|
if (!ctrl_pdata) {
|
|
ctrl_pdata = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct mdss_dsi_ctrl_pdata), GFP_KERNEL);
|
|
if (!ctrl_pdata) {
|
|
pr_err("%s: FAILED: cannot alloc dsi ctrl\n", __func__);
|
|
rc = -ENOMEM;
|
|
goto error_no_mem;
|
|
}
|
|
platform_set_drvdata(pdev, ctrl_pdata);
|
|
}
|
|
|
|
ctrl_pdata->mdss_util = mdss_get_util_intf();
|
|
if (mdp3_res->mdss_util == NULL) {
|
|
pr_err("Failed to get mdss utility functions\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
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__);
|
|
rc = -ENOMEM;
|
|
goto error_io_resource;
|
|
} else {
|
|
dsi_host_private->dsi_reg_size = resource_size(mdss_dsi_mres);
|
|
dsi_host_private->dsi_base = ioremap(mdss_dsi_mres->start,
|
|
dsi_host_private->dsi_reg_size);
|
|
if (!dsi_host_private->dsi_base) {
|
|
pr_err("%s:%d unable to remap dsi resources",
|
|
__func__, __LINE__);
|
|
rc = -ENOMEM;
|
|
goto error_io_resource;
|
|
}
|
|
}
|
|
|
|
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 error_irq_resource;
|
|
}
|
|
|
|
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 error_platform_pop;
|
|
}
|
|
|
|
/* DSI panels can be different between controllers */
|
|
rc = dsi_get_panel_cfg(panel_cfg);
|
|
if (!rc)
|
|
/* dsi panel cfg not present */
|
|
pr_warn("%s:%d:dsi specific cfg not present\n",
|
|
__func__, __LINE__);
|
|
|
|
/* find panel device node */
|
|
dsi_pan_node = dsi_find_panel_of_node(pdev, panel_cfg);
|
|
if (!dsi_pan_node) {
|
|
pr_err("%s: can't find panel node %s\n", __func__,
|
|
panel_cfg);
|
|
goto error_pan_node;
|
|
}
|
|
|
|
cmd_cfg_cont_splash = mdp3_panel_get_boot_cfg() ? true : false;
|
|
|
|
rc = mdss_dsi_panel_init(dsi_pan_node, ctrl_pdata, cmd_cfg_cont_splash);
|
|
if (rc) {
|
|
pr_err("%s: dsi panel init failed\n", __func__);
|
|
goto error_pan_node;
|
|
}
|
|
|
|
rc = dsi_ctrl_config_init(pdev, ctrl_pdata);
|
|
if (rc) {
|
|
dev_err(&pdev->dev, "%s: failed to parse mdss dtsi rc=%d\n",
|
|
__func__, rc);
|
|
goto error_pan_node;
|
|
}
|
|
|
|
msm_dsi_parse_lane_swap(pdev->dev.of_node, &(ctrl_pdata->dlane_swap));
|
|
|
|
for (i = 0; i < DSI_MAX_PM; i++) {
|
|
rc = msm_dsi_io_init(pdev, &(ctrl_pdata->power_data[i]));
|
|
if (rc) {
|
|
dev_err(&pdev->dev, "%s: failed to init IO for %s\n",
|
|
__func__, __mdss_dsi_pm_name(i));
|
|
goto error_io_init;
|
|
}
|
|
}
|
|
|
|
pr_debug("%s: Dsi Ctrl->0 initialized\n", __func__);
|
|
|
|
dsi_host_private->dis_dev = pdev->dev;
|
|
intf.on = msm_dsi_on;
|
|
intf.off = msm_dsi_off;
|
|
intf.cont_on = msm_dsi_cont_on;
|
|
intf.clk_ctrl = msm_dsi_clk_ctrl;
|
|
intf.op_mode_config = msm_dsi_op_mode_config;
|
|
intf.index = 0;
|
|
intf.private = NULL;
|
|
dsi_register_interface(&intf);
|
|
|
|
msm_dsi_debug_init();
|
|
|
|
msm_dsi_ctrl_init(ctrl_pdata);
|
|
|
|
rc = msm_dsi_irq_init(&pdev->dev, mdss_dsi_mres->start,
|
|
ctrl_pdata);
|
|
if (rc) {
|
|
dev_err(&pdev->dev, "%s: failed to init irq, rc=%d\n",
|
|
__func__, rc);
|
|
goto error_irq_init;
|
|
}
|
|
|
|
rc = dsi_panel_device_register_v2(pdev, ctrl_pdata);
|
|
if (rc) {
|
|
pr_err("%s: dsi panel dev reg failed\n", __func__);
|
|
goto error_device_register;
|
|
}
|
|
pr_debug("%s success\n", __func__);
|
|
return 0;
|
|
error_device_register:
|
|
kfree(ctrl_pdata->dsi_hw->irq_info);
|
|
kfree(ctrl_pdata->dsi_hw);
|
|
error_irq_init:
|
|
for (i = DSI_MAX_PM - 1; i >= 0; i--)
|
|
msm_dsi_io_deinit(pdev, &(ctrl_pdata->power_data[i]));
|
|
error_io_init:
|
|
dsi_ctrl_config_deinit(pdev, ctrl_pdata);
|
|
error_pan_node:
|
|
of_node_put(dsi_pan_node);
|
|
error_platform_pop:
|
|
msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask);
|
|
error_irq_resource:
|
|
if (dsi_host_private->dsi_base) {
|
|
iounmap(dsi_host_private->dsi_base);
|
|
dsi_host_private->dsi_base = NULL;
|
|
}
|
|
error_io_resource:
|
|
devm_kfree(&pdev->dev, ctrl_pdata);
|
|
error_no_mem:
|
|
msm_dsi_deinit();
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int msm_dsi_remove(struct platform_device *pdev)
|
|
{
|
|
int i;
|
|
|
|
struct mdss_dsi_ctrl_pdata *ctrl_pdata = platform_get_drvdata(pdev);
|
|
if (!ctrl_pdata) {
|
|
pr_err("%s: no driver data\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
msm_dsi_clear_irq(ctrl_pdata, ctrl_pdata->dsi_irq_mask);
|
|
for (i = DSI_MAX_PM - 1; i >= 0; i--)
|
|
msm_dsi_io_deinit(pdev, &(ctrl_pdata->power_data[i]));
|
|
dsi_ctrl_config_deinit(pdev, ctrl_pdata);
|
|
iounmap(dsi_host_private->dsi_base);
|
|
dsi_host_private->dsi_base = NULL;
|
|
msm_dsi_deinit();
|
|
devm_kfree(&pdev->dev, ctrl_pdata);
|
|
|
|
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 = 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);
|