/* 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 #include #include #include #include #include #include #include #include #include #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 '' 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);