/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include "mdss.h" #include "mdss_dsi.h" static struct mdss_dsi_ctrl_pdata *left_ctrl_pdata; static struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; struct mdss_hw mdss_dsi0_hw = { .hw_ndx = MDSS_HW_DSI0, .ptr = NULL, .irq_handler = mdss_dsi_isr, }; struct mdss_hw mdss_dsi1_hw = { .hw_ndx = MDSS_HW_DSI1, .ptr = NULL, .irq_handler = mdss_dsi_isr, }; void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) { if (ctrl->panel_data.panel_info.pdest == DISPLAY_1) { mdss_dsi0_hw.ptr = (void *)(ctrl); ctrl->dsi_hw = &mdss_dsi0_hw; ctrl->ndx = DSI_CTRL_0; } else { mdss_dsi1_hw.ptr = (void *)(ctrl); ctrl->dsi_hw = &mdss_dsi1_hw; ctrl->ndx = DSI_CTRL_1; } ctrl_list[ctrl->ndx] = ctrl; /* keep it */ if (mdss_register_irq(ctrl->dsi_hw)) pr_err("%s: mdss_register_irq failed.\n", __func__); pr_debug("%s: ndx=%d base=%p\n", __func__, ctrl->ndx, ctrl->ctrl_base); init_completion(&ctrl->dma_comp); init_completion(&ctrl->mdp_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); mdss_dsi_buf_alloc(&ctrl->tx_buf, SZ_4K); mdss_dsi_buf_alloc(&ctrl->rx_buf, SZ_4K); } /* * acquire ctrl->mutex first */ void mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable) { mutex_lock(&ctrl->mutex); if (enable) { if (ctrl->clk_cnt == 0) { mdss_dsi_prepare_clocks(ctrl); mdss_dsi_clk_enable(ctrl); } ctrl->clk_cnt++; } else { if (ctrl->clk_cnt) { ctrl->clk_cnt--; if (ctrl->clk_cnt == 0) { mdss_dsi_clk_disable(ctrl); mdss_dsi_unprepare_clocks(ctrl); } } } pr_debug("%s: ctrl ndx=%d enabled=%d clk_cnt=%d\n", __func__, ctrl->ndx, enable, ctrl->clk_cnt); mutex_unlock(&ctrl->mutex); } void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable) { if (enable == 0) { /* need wait before disable */ mutex_lock(&ctrl->cmd_mutex); mdss_dsi_cmd_mdp_busy(ctrl); mutex_unlock(&ctrl->cmd_mutex); } mdss_dsi_clk_ctrl(ctrl, enable); } void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) { unsigned long flags; spin_lock_irqsave(&ctrl->irq_lock, flags); if (ctrl->dsi_irq_mask & term) { spin_unlock_irqrestore(&ctrl->irq_lock, flags); return; } if (ctrl->dsi_irq_mask == 0) { mdss_enable_irq(ctrl->dsi_hw); pr_debug("%s: IRQ Enable, ndx=%d mask=%x term=%x\n", __func__, ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term); } ctrl->dsi_irq_mask |= term; spin_unlock_irqrestore(&ctrl->irq_lock, flags); } void mdss_dsi_disable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) { unsigned long flags; spin_lock_irqsave(&ctrl->irq_lock, flags); if (!(ctrl->dsi_irq_mask & term)) { spin_unlock_irqrestore(&ctrl->irq_lock, flags); return; } ctrl->dsi_irq_mask &= ~term; if (ctrl->dsi_irq_mask == 0) { mdss_disable_irq(ctrl->dsi_hw); pr_debug("%s: IRQ Disable, ndx=%d mask=%x term=%x\n", __func__, ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term); } spin_unlock_irqrestore(&ctrl->irq_lock, flags); } /* * mdss_dsi_disale_irq_nosync() should be called * from interrupt context */ void mdss_dsi_disable_irq_nosync(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) { spin_lock(&ctrl->irq_lock); if (!(ctrl->dsi_irq_mask & term)) { spin_unlock(&ctrl->irq_lock); return; } ctrl->dsi_irq_mask &= ~term; if (ctrl->dsi_irq_mask == 0) { mdss_disable_irq_nosync(ctrl->dsi_hw); pr_debug("%s: IRQ Disable, ndx=%d mask=%x term=%x\n", __func__, ctrl->ndx, (int)ctrl->dsi_irq_mask, (int)term); } spin_unlock(&ctrl->irq_lock); } /* * mipi dsi buf mechanism */ char *mdss_dsi_buf_reserve(struct dsi_buf *dp, int len) { dp->data += len; return dp->data; } char *mdss_dsi_buf_unreserve(struct dsi_buf *dp, int len) { dp->data -= len; return dp->data; } char *mdss_dsi_buf_push(struct dsi_buf *dp, int len) { dp->data -= len; dp->len += len; return dp->data; } char *mdss_dsi_buf_reserve_hdr(struct dsi_buf *dp, int hlen) { dp->hdr = (u32 *)dp->data; return mdss_dsi_buf_reserve(dp, hlen); } char *mdss_dsi_buf_init(struct dsi_buf *dp) { int off; dp->data = dp->start; off = (int)dp->data; /* 8 byte align */ off &= 0x07; if (off) off = 8 - off; dp->data += off; dp->len = 0; return dp->data; } int mdss_dsi_buf_alloc(struct dsi_buf *dp, int size) { dp->start = dma_alloc_writecombine(NULL, size, &dp->dmap, GFP_KERNEL); if (dp->start == NULL) { pr_err("%s:%u\n", __func__, __LINE__); return -ENOMEM; } dp->end = dp->start + size; dp->size = size; if ((int)dp->start & 0x07) pr_err("%s: buf NOT 8 bytes aligned\n", __func__); dp->data = dp->start; dp->len = 0; return size; } /* * mipi dsi generic long write */ static int mdss_dsi_generic_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; char *bp; u32 *hp; int i, len = 0; dchdr = &cm->dchdr; bp = mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); /* fill up payload */ if (cm->payload) { len = dchdr->dlen; len += 3; len &= ~0x03; /* multipled by 4 */ for (i = 0; i < dchdr->dlen; i++) *bp++ = cm->payload[i]; /* append 0xff to the end */ for (; i < len; i++) *bp++ = 0xff; dp->len += len; } /* fill up header */ hp = dp->hdr; *hp = 0; *hp = DSI_HDR_WC(dchdr->dlen); *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_LONG_PKT; *hp |= DSI_HDR_DTYPE(DTYPE_GEN_LWRITE); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); len += DSI_HOST_HDR_SIZE; return len; } /* * mipi dsi generic short write with 0, 1 2 parameters */ static int mdss_dsi_generic_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; int len; dchdr = &cm->dchdr; if (dchdr->dlen && cm->payload == 0) { pr_err("%s: NO payload error\n", __func__); return 0; } mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); if (dchdr->last) *hp |= DSI_HDR_LAST; len = (dchdr->dlen > 2) ? 2 : dchdr->dlen; if (len == 1) { *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE1); *hp |= DSI_HDR_DATA1(cm->payload[0]); *hp |= DSI_HDR_DATA2(0); } else if (len == 2) { *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE2); *hp |= DSI_HDR_DATA1(cm->payload[0]); *hp |= DSI_HDR_DATA2(cm->payload[1]); } else { *hp |= DSI_HDR_DTYPE(DTYPE_GEN_WRITE); *hp |= DSI_HDR_DATA1(0); *hp |= DSI_HDR_DATA2(0); } mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } /* * mipi dsi gerneric read with 0, 1 2 parameters */ static int mdss_dsi_generic_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; int len; dchdr = &cm->dchdr; if (dchdr->dlen && cm->payload == 0) { pr_err("%s: NO payload error\n", __func__); return 0; } mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_BTA; if (dchdr->last) *hp |= DSI_HDR_LAST; len = (dchdr->dlen > 2) ? 2 : dchdr->dlen; if (len == 1) { *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ1); *hp |= DSI_HDR_DATA1(cm->payload[0]); *hp |= DSI_HDR_DATA2(0); } else if (len == 2) { *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ2); *hp |= DSI_HDR_DATA1(cm->payload[0]); *hp |= DSI_HDR_DATA2(cm->payload[1]); } else { *hp |= DSI_HDR_DTYPE(DTYPE_GEN_READ); *hp |= DSI_HDR_DATA1(0); *hp |= DSI_HDR_DATA2(0); } mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } /* * mipi dsi dcs long write */ static int mdss_dsi_dcs_lwrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; char *bp; u32 *hp; int i, len = 0; dchdr = &cm->dchdr; bp = mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); /* * fill up payload * dcs command byte (first byte) followed by payload */ if (cm->payload) { len = dchdr->dlen; len += 3; len &= ~0x03; /* multipled by 4 */ for (i = 0; i < dchdr->dlen; i++) *bp++ = cm->payload[i]; /* append 0xff to the end */ for (; i < len; i++) *bp++ = 0xff; dp->len += len; } /* fill up header */ hp = dp->hdr; *hp = 0; *hp = DSI_HDR_WC(dchdr->dlen); *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_LONG_PKT; *hp |= DSI_HDR_DTYPE(DTYPE_DCS_LWRITE); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); len += DSI_HOST_HDR_SIZE; return len; } /* * mipi dsi dcs short write with 0 parameters */ static int mdss_dsi_dcs_swrite(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; int len; dchdr = &cm->dchdr; if (cm->payload == 0) { pr_err("%s: NO payload error\n", __func__); return -EINVAL; } mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); if (dchdr->ack) /* ask ACK trigger msg from peripeheral */ *hp |= DSI_HDR_BTA; if (dchdr->last) *hp |= DSI_HDR_LAST; len = (dchdr->dlen > 1) ? 1 : dchdr->dlen; *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE); *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */ *hp |= DSI_HDR_DATA2(0); mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } /* * mipi dsi dcs short write with 1 parameters */ static int mdss_dsi_dcs_swrite1(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; if (dchdr->dlen < 2 || cm->payload == 0) { pr_err("%s: NO payload error\n", __func__); return -EINVAL; } mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); if (dchdr->ack) /* ask ACK trigger msg from peripeheral */ *hp |= DSI_HDR_BTA; if (dchdr->last) *hp |= DSI_HDR_LAST; *hp |= DSI_HDR_DTYPE(DTYPE_DCS_WRITE1); *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs comamnd byte */ *hp |= DSI_HDR_DATA2(cm->payload[1]); /* parameter */ mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } /* * mipi dsi dcs read with 0 parameters */ static int mdss_dsi_dcs_read(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; if (cm->payload == 0) { pr_err("%s: NO payload error\n", __func__); return -EINVAL; } mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_BTA; *hp |= DSI_HDR_DTYPE(DTYPE_DCS_READ); if (dchdr->last) *hp |= DSI_HDR_LAST; *hp |= DSI_HDR_DATA1(cm->payload[0]); /* dcs command byte */ *hp |= DSI_HDR_DATA2(0); mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_cm_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_CM_ON); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_cm_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_CM_OFF); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_peripheral_on(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_ON); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_peripheral_off(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_PERIPHERAL_OFF); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_set_max_pktsize(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; if (cm->payload == 0) { pr_err("%s: NO payload error\n", __func__); return 0; } mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_MAX_PKTSIZE); if (dchdr->last) *hp |= DSI_HDR_LAST; *hp |= DSI_HDR_DATA1(cm->payload[0]); *hp |= DSI_HDR_DATA2(cm->payload[1]); mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_null_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp = DSI_HDR_WC(dchdr->dlen); *hp |= DSI_HDR_LONG_PKT; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_NULL_PKT); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } static int mdss_dsi_blank_pkt(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; u32 *hp; dchdr = &cm->dchdr; mdss_dsi_buf_reserve_hdr(dp, DSI_HOST_HDR_SIZE); hp = dp->hdr; *hp = 0; *hp = DSI_HDR_WC(dchdr->dlen); *hp |= DSI_HDR_LONG_PKT; *hp |= DSI_HDR_VC(dchdr->vc); *hp |= DSI_HDR_DTYPE(DTYPE_BLANK_PKT); if (dchdr->last) *hp |= DSI_HDR_LAST; mdss_dsi_buf_push(dp, DSI_HOST_HDR_SIZE); return DSI_HOST_HDR_SIZE; /* 4 bytes */ } /* * prepare cmd buffer to be txed */ int mdss_dsi_cmd_dma_add(struct dsi_buf *dp, struct dsi_cmd_desc *cm) { struct dsi_ctrl_hdr *dchdr; int len = 0; dchdr = &cm->dchdr; switch (dchdr->dtype) { case DTYPE_GEN_WRITE: case DTYPE_GEN_WRITE1: case DTYPE_GEN_WRITE2: len = mdss_dsi_generic_swrite(dp, cm); break; case DTYPE_GEN_LWRITE: len = mdss_dsi_generic_lwrite(dp, cm); break; case DTYPE_GEN_READ: case DTYPE_GEN_READ1: case DTYPE_GEN_READ2: len = mdss_dsi_generic_read(dp, cm); break; case DTYPE_DCS_LWRITE: len = mdss_dsi_dcs_lwrite(dp, cm); break; case DTYPE_DCS_WRITE: len = mdss_dsi_dcs_swrite(dp, cm); break; case DTYPE_DCS_WRITE1: len = mdss_dsi_dcs_swrite1(dp, cm); break; case DTYPE_DCS_READ: len = mdss_dsi_dcs_read(dp, cm); break; case DTYPE_MAX_PKTSIZE: len = mdss_dsi_set_max_pktsize(dp, cm); break; case DTYPE_NULL_PKT: len = mdss_dsi_null_pkt(dp, cm); break; case DTYPE_BLANK_PKT: len = mdss_dsi_blank_pkt(dp, cm); break; case DTYPE_CM_ON: len = mdss_dsi_cm_on(dp, cm); break; case DTYPE_CM_OFF: len = mdss_dsi_cm_off(dp, cm); break; case DTYPE_PERIPHERAL_ON: len = mdss_dsi_peripheral_on(dp, cm); break; case DTYPE_PERIPHERAL_OFF: len = mdss_dsi_peripheral_off(dp, cm); break; default: pr_debug("%s: dtype=%x NOT supported\n", __func__, dchdr->dtype); break; } return len; } /* * mdss_dsi_short_read1_resp: 1 parameter */ static int mdss_dsi_short_read1_resp(struct dsi_buf *rp) { /* strip out dcs type */ rp->data++; rp->len = 1; return rp->len; } /* * mdss_dsi_short_read2_resp: 2 parameter */ static int mdss_dsi_short_read2_resp(struct dsi_buf *rp) { /* strip out dcs type */ rp->data++; rp->len = 2; return rp->len; } static int mdss_dsi_long_read_resp(struct dsi_buf *rp) { short len; len = rp->data[2]; len <<= 8; len |= rp->data[1]; /* strip out dcs header */ rp->data += 4; rp->len -= 4; /* strip out 2 bytes of checksum */ rp->len -= 2; return len; } void mdss_dsi_cmd_test_pattern(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; int i; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x015c, 0x201); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x016c, 0xff0000); /* red */ i = 0; while (i++ < 50) { MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0184, 0x1); /* Add sleep to get ~50 fps frame rate*/ msleep(20); } MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x015c, 0x0); } void mdss_dsi_host_init(struct mipi_panel_info *pinfo, struct mdss_panel_data *pdata) { u32 dsi_ctrl, intr_ctrl; u32 data; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); pinfo->rgb_swap = DSI_RGB_SWAP_RGB; if (pinfo->mode == DSI_VIDEO_MODE) { data = 0; if (pinfo->pulse_mode_hsa_he) data |= BIT(28); if (pinfo->hfp_power_stop) data |= BIT(24); if (pinfo->hbp_power_stop) data |= BIT(20); if (pinfo->hsa_power_stop) data |= BIT(16); if (pinfo->eof_bllp_power_stop) data |= BIT(15); if (pinfo->bllp_power_stop) data |= BIT(12); data |= ((pinfo->traffic_mode & 0x03) << 8); data |= ((pinfo->dst_format & 0x03) << 4); /* 2 bits */ data |= (pinfo->vc & 0x03); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0010, 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_pdata->ctrl_base) + 0x0020, 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_pdata->ctrl_base) + 0x0040, 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_pdata->ctrl_base) + 0x0044, data); } else pr_err("%s: Unknown DSI mode=%d\n", __func__, pinfo->mode); dsi_ctrl = BIT(8) | BIT(2); /* clock enable & cmd mode */ intr_ctrl = 0; intr_ctrl = (DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_CMD_MDP_DONE_MASK); if (pinfo->crc_check) dsi_ctrl |= BIT(24); if (pinfo->ecc_check) dsi_ctrl |= BIT(20); if (pinfo->data_lane3) dsi_ctrl |= BIT(7); if (pinfo->data_lane2) dsi_ctrl |= BIT(6); if (pinfo->data_lane1) dsi_ctrl |= BIT(5); if (pinfo->data_lane0) dsi_ctrl |= BIT(4); /* from frame buffer, low power mode */ /* DSI_COMMAND_MODE_DMA_CTRL */ if (ctrl_pdata->shared_pdata.broadcast_enable) MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x94000000); else MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x14000000); if (ctrl_pdata->shared_pdata.broadcast_enable) if (pdata->panel_info.pdest == DISPLAY_1) { pr_debug("%s: Broadcast mode enabled.\n", __func__); left_ctrl_pdata = ctrl_pdata; } 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_pdata->ctrl_base) + 0x0084, data); /* DSI_TRIG_CTRL */ /* DSI_LAN_SWAP_CTRL */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x00b0, pinfo->dlane_swap); /* clock out ctrl */ data = pinfo->t_clk_post & 0x3f; /* 6 bits */ data <<= 8; data |= pinfo->t_clk_pre & 0x3f; /* 6 bits */ /* DSI_CLKOUT_TIMING_CTRL */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xc4, data); data = 0; if (pinfo->rx_eot_ignore) data |= BIT(4); if (pinfo->tx_eot_append) data |= BIT(0); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x00cc, data); /* DSI_EOT_PACKET_CTRL */ /* allow only ack-err-status to generate interrupt */ /* DSI_ERR_INT_MASK0 */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x010c, 0x13ff3fe0); intr_ctrl |= DSI_INTR_ERROR_MASK; MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, intr_ctrl); /* DSI_INTL_CTRL */ /* turn esc, byte, dsi, pclk, sclk, hclk on */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x11c, 0x23f); /* DSI_CLK_CTRL */ dsi_ctrl |= BIT(0); /* enable dsi */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); wmb(); } void mdss_set_tx_power_mode(int mode, struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; u32 data; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); data = MIPI_INP((ctrl_pdata->ctrl_base) + 0x3c); if (mode == 0) data &= ~BIT(26); else data |= BIT(26); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x3c, data); } void mdss_dsi_sw_reset(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; u32 dsi_ctrl; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004); dsi_ctrl &= ~0x01; MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); wmb(); /* turn esc, byte, dsi, pclk, sclk, hclk on */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x11c, 0x23f); /* DSI_CLK_CTRL */ wmb(); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x118, 0x01); wmb(); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x118, 0x00); wmb(); } void mdss_dsi_controller_cfg(int enable, struct mdss_panel_data *pdata) { u32 dsi_ctrl; u32 status; u32 sleep_us = 1000; u32 timeout_us = 16000; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); /* Check for CMD_MODE_DMA_BUSY */ if (readl_poll_timeout(((ctrl_pdata->ctrl_base) + 0x0008), status, ((status & 0x02) == 0), sleep_us, timeout_us)) pr_info("%s: DSI status=%x failed\n", __func__, status); /* Check for x_HS_FIFO_EMPTY */ if (readl_poll_timeout(((ctrl_pdata->ctrl_base) + 0x000c), status, ((status & 0x11111000) == 0x11111000), sleep_us, timeout_us)) pr_info("%s: FIFO status=%x failed\n", __func__, status); /* Check for VIDEO_MODE_ENGINE_BUSY */ if (readl_poll_timeout(((ctrl_pdata->ctrl_base) + 0x0008), status, ((status & 0x08) == 0), sleep_us, timeout_us)) { pr_debug("%s: DSI status=%x\n", __func__, status); pr_debug("%s: Doing sw reset\n", __func__); mdss_dsi_sw_reset(pdata); } dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004); if (enable) dsi_ctrl |= 0x01; else dsi_ctrl &= ~0x01; MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); wmb(); } void mdss_dsi_op_mode_config(int mode, struct mdss_panel_data *pdata) { u32 dsi_ctrl, intr_ctrl; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); if (ctrl_pdata->shared_pdata.broadcast_enable) if (pdata->panel_info.pdest == DISPLAY_1) { pr_debug("%s: Broadcast mode. 1st ctrl\n", __func__); return; } dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004); /*If Video enabled, Keep Video and Cmd mode ON */ if (dsi_ctrl & 0x02) dsi_ctrl &= ~0x05; else dsi_ctrl &= ~0x07; if (mode == DSI_VIDEO_MODE) { dsi_ctrl |= 0x03; intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK; } else { /* command mode */ dsi_ctrl |= 0x05; if (pdata->panel_info.type == MIPI_VIDEO_PANEL) dsi_ctrl |= 0x02; intr_ctrl = DSI_INTR_CMD_DMA_DONE_MASK | DSI_INTR_ERROR_MASK | DSI_INTR_CMD_MDP_DONE_MASK; } if (ctrl_pdata->shared_pdata.broadcast_enable) if ((pdata->panel_info.pdest == DISPLAY_2) && (left_ctrl_pdata != NULL)) { MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110, intr_ctrl); /* DSI_INTL_CTRL */ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004, dsi_ctrl); } MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, intr_ctrl); /* DSI_INTL_CTRL */ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); wmb(); } void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata) { u32 status; int timeout_us = 10000; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x098, 0x01); /* trigger */ wmb(); /* Check for CMD_MODE_DMA_BUSY */ if (readl_poll_timeout(((ctrl_pdata->ctrl_base) + 0x0008), status, ((status & 0x0010) == 0), 0, timeout_us)) pr_info("%s: DSI status=%x failed\n", __func__, status); mdss_dsi_ack_err_status((ctrl_pdata->ctrl_base)); pr_debug("%s: BTA done, status = %d\n", __func__, status); } int mdss_dsi_cmd_reg_tx(u32 data, unsigned char *ctrl_base) { int i; char *bp; bp = (char *)&data; pr_debug("%s: ", __func__); for (i = 0; i < 4; i++) pr_debug("%x ", *bp++); pr_debug("\n"); MIPI_OUTP(ctrl_base + 0x0084, 0x04);/* sw trigger */ MIPI_OUTP(ctrl_base + 0x0004, 0x135); wmb(); MIPI_OUTP(ctrl_base + 0x03c, data); wmb(); MIPI_OUTP(ctrl_base + 0x090, 0x01); /* trigger */ wmb(); udelay(300); return 4; } static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_buf *tp); static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_buf *rp, int rlen); static int mdss_dsi_cmds2buf_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, tot = 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); tot += len; if (dchdr->last) { tp->data = tp->start; /* begin of buf */ mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM); mdss_dsi_cmd_dma_tx(ctrl, tp); if (dchdr->wait) usleep(dchdr->wait * 1000); mdss_dsi_buf_init(tp); len = 0; } cm++; } return tot; } /* * mdss_dsi_cmds_tx: * thread context only */ int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_cmd_desc *cmds, int cnt) { u32 dsi_ctrl, data; int video_mode; if (ctrl->shared_pdata.broadcast_enable) { if (ctrl->ndx == DSI_CTRL_0) { pr_debug("%s: Broadcast mode. 1st ctrl\n", __func__); return 0; } } if (ctrl->shared_pdata.broadcast_enable) { if ((ctrl->ndx == DSI_CTRL_1) && (left_ctrl_pdata != NULL)) { dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base + 0x0004); video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */ if (video_mode) { data = dsi_ctrl | 0x04; /* CMD_MODE_EN */ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004, data); } } } /* turn on cmd mode * for video mode, do not send cmds more than * one pixel line, since it only transmit it * during BLLP. */ dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004); video_mode = dsi_ctrl & 0x02; /* VIDEO_MODE_EN */ if (video_mode) { data = dsi_ctrl | 0x04; /* CMD_MODE_EN */ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, data); } mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt); if (video_mode) MIPI_OUTP((ctrl->ctrl_base) + 0x0004, dsi_ctrl); /* restore */ return cnt; } /* 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, }; /* * DSI panel reply with MAX_RETURN_PACKET_SIZE bytes of data * plus DCS header, ECC and CRC for DCS long read response * mdss_dsi_controller only have 4x32 bits register ( 16 bytes) to * hold data per transaction. * MIPI_DSI_LEN equal to 8 * len should be either 4 or 8 * any return data more than MIPI_DSI_LEN need to be break down * to multiple transactions. * * ov_mutex need to be acquired before call this function. */ int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_cmd_desc *cmds, int rlen, u32 rx_flags) { int cnt, len, diff, pkt_size; struct dsi_buf *tp, *rp; int no_max_pkt_size; char cmd; no_max_pkt_size = rx_flags & CMD_REQ_NO_MAX_PKT_SIZE; if (no_max_pkt_size) rlen = ALIGN(rlen, 4); /* Only support rlen = 4*n */ len = rlen; diff = 0; if (len <= 2) cnt = 4; /* short read */ else { if (len > MDSS_DSI_LEN) len = MDSS_DSI_LEN; /* 8 bytes at most */ len = ALIGN(len, 4); /* len 4 bytes align */ diff = len - rlen; /* * add extra 2 bytes to len to have overall * packet size is multipe by 4. This also make * sure 4 bytes dcs headerlocates within a * 32 bits register after shift in. * after all, len should be either 6 or 10. */ len += 2; cnt = len + 6; /* 4 bytes header + 2 bytes crc */ } tp = &ctrl->tx_buf; rp = &ctrl->rx_buf; if (!no_max_pkt_size) { /* packet size need to be set at every read */ pkt_size = len; max_pktsize[0] = pkt_size; mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM); mdss_dsi_buf_init(tp); mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd); mdss_dsi_cmd_dma_tx(ctrl, tp); pr_debug("%s: Max packet size sent\n", __func__); } mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM); mdss_dsi_buf_init(tp); mdss_dsi_cmd_dma_add(tp, cmds); /* transmit read comamnd to client */ mdss_dsi_cmd_dma_tx(ctrl, tp); /* * once cmd_dma_done interrupt received, * return data from client is ready and stored * at RDBK_DATA register already */ mdss_dsi_buf_init(rp); if (no_max_pkt_size) { /* * expect rlen = n * 4 * short alignement for start addr */ rp->data += 2; } mdss_dsi_cmd_dma_rx(ctrl, rp, cnt); if (no_max_pkt_size) { /* * remove extra 2 bytes from previous * rx transaction at shift register * which was inserted during copy * shift registers to rx buffer * rx payload start from long alignment addr */ rp->data += 2; } cmd = rp->data[0]; switch (cmd) { case DTYPE_ACK_ERR_RESP: pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__); break; case DTYPE_GEN_READ1_RESP: case DTYPE_DCS_READ1_RESP: 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); rp->len -= 2; /* extra 2 bytes added */ rp->len -= diff; /* align bytes */ break; default: pr_debug("%s: Unknown cmd received\n", __func__); break; } return rp->len; } #define DMA_TX_TIMEOUT 200 static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_buf *tp) { int len; int domain = MDSS_IOMMU_DOMAIN_UNSECURE; char *bp; unsigned long size, addr; bp = tp->data; len = ALIGN(tp->len, 4); size = ALIGN(tp->len, SZ_4K); if (is_mdss_iommu_attached()) { int ret = msm_iommu_map_contig_buffer(tp->dmap, mdss_get_iommu_domain(domain), 0, size, SZ_4K, 0, &(addr)); if (IS_ERR_VALUE(ret)) { pr_err("unable to map dma memory to iommu(%d)\n", ret); return -ENOMEM; } } else { addr = tp->dmap; } INIT_COMPLETION(ctrl->dma_comp); if (ctrl->shared_pdata.broadcast_enable) if ((ctrl->ndx == DSI_CTRL_1) && (left_ctrl_pdata != NULL)) { MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x048, addr); MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x04c, len); } MIPI_OUTP((ctrl->ctrl_base) + 0x048, addr); MIPI_OUTP((ctrl->ctrl_base) + 0x04c, len); wmb(); if (ctrl->shared_pdata.broadcast_enable) if ((ctrl->ndx == DSI_CTRL_1) && (left_ctrl_pdata != NULL)) { MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x090, 0x01); } MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); /* trigger */ wmb(); if (!wait_for_completion_timeout(&ctrl->dma_comp, msecs_to_jiffies(DMA_TX_TIMEOUT))) { pr_err("%s: dma timeout error\n", __func__); } if (is_mdss_iommu_attached()) msm_iommu_unmap_contig_buffer(addr, mdss_get_iommu_domain(domain), 0, size); return tp->len; } static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_buf *rp, int rlen) { u32 *lp, data; int i, off, cnt; lp = (u32 *)rp->data; cnt = rlen; cnt += 3; cnt >>= 2; if (cnt > 4) cnt = 4; /* 4 x 32 bits registers only */ off = 0x06c; /* DSI_RDBK_DATA0 */ off += ((cnt - 1) * 4); for (i = 0; i < cnt; i++) { data = (u32)MIPI_INP((ctrl->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; } #define VSYNC_PERIOD 17 void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl) { unsigned long flag; u32 data; /* DSI_INTL_CTRL */ data = MIPI_INP((ctrl->ctrl_base) + 0x0110); data |= DSI_INTR_VIDEO_DONE_MASK; MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data); spin_lock_irqsave(&ctrl->mdp_lock, flag); INIT_COMPLETION(ctrl->video_comp); mdss_dsi_enable_irq(ctrl, DSI_VIDEO_TERM); spin_unlock_irqrestore(&ctrl->mdp_lock, flag); wait_for_completion_timeout(&ctrl->video_comp, msecs_to_jiffies(VSYNC_PERIOD * 4)); data = MIPI_INP((ctrl->ctrl_base) + 0x0110); data &= ~DSI_INTR_VIDEO_DONE_MASK; MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data); } static void mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl) { mdss_dsi_wait4video_done(ctrl); /* delay 4 ms to skip BLLP */ usleep(4000); } void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl) { unsigned long flag; spin_lock_irqsave(&ctrl->mdp_lock, flag); mdss_dsi_enable_irq(ctrl, DSI_MDP_TERM); ctrl->mdp_busy = true; INIT_COMPLETION(ctrl->mdp_comp); spin_unlock_irqrestore(&ctrl->mdp_lock, flag); } void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) { unsigned long flags; int need_wait = 0; pr_debug("%s: start pid=%d\n", __func__, current->pid); spin_lock_irqsave(&ctrl->mdp_lock, flags); if (ctrl->mdp_busy == true) need_wait++; spin_unlock_irqrestore(&ctrl->mdp_lock, flags); if (need_wait) { /* wait until DMA finishes the current job */ pr_debug("%s: pending pid=%d\n", __func__, current->pid); wait_for_completion(&ctrl->mdp_comp); } pr_debug("%s: done pid=%d\n", __func__, current->pid); } void mdss_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl, struct dcs_cmd_req *req) { int ret; ret = mdss_dsi_cmds_tx(ctrl, req->cmds, req->cmds_cnt); if (req->cb) req->cb(ret); } void mdss_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl, struct dcs_cmd_req *req) { int len; u32 data, *dp; struct dsi_buf *rp; len = mdss_dsi_cmds_rx(ctrl, req->cmds, req->rlen, req->flags); rp = &ctrl->rx_buf; dp = (u32 *)rp->data; data = *dp; if (req->cb) req->cb(data); } void mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) { struct dcs_cmd_req *req; u32 data; mutex_lock(&ctrl->cmd_mutex); req = mdss_dsi_cmdlist_get(ctrl); /* make sure dsi_cmd_mdp is idle */ mdss_dsi_cmd_mdp_busy(ctrl); if (req == NULL) goto need_lock; pr_debug("%s: from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid); mdss_dsi_clk_ctrl(ctrl, 1); data = MIPI_INP((ctrl->ctrl_base) + 0x0004); if (data & 0x02) { /* video mode, make sure video engine is busy * so dcs command will be sent at start of BLLP */ mdss_dsi_wait4video_eng_busy(ctrl); } else { /* command mode */ if (!from_mdp) { /* cmdlist_put */ /* make sure dsi_cmd_mdp is idle */ mdss_dsi_cmd_mdp_busy(ctrl); } } if (req->flags & CMD_REQ_RX) mdss_dsi_cmdlist_rx(ctrl, req); else mdss_dsi_cmdlist_tx(ctrl, req); mdss_dsi_clk_ctrl(ctrl, 0); need_lock: if (from_mdp) /* from pipe_commit */ mdss_dsi_cmd_mdp_start(ctrl); mutex_unlock(&ctrl->cmd_mutex); } /* * mdss_dsi_cmd_get: ctrl->cmd_mutex acquired by caller */ struct dcs_cmd_req *mdss_dsi_cmdlist_get(struct mdss_dsi_ctrl_pdata *ctrl) { struct dcs_cmd_list *clist; struct dcs_cmd_req *req = NULL; clist = &ctrl->cmdlist; if (clist->get != clist->put) { req = &clist->list[clist->get]; clist->get++; clist->get %= CMD_REQ_MAX; clist->tot--; pr_debug("%s: tot=%d put=%d get=%d\n", __func__, clist->tot, clist->put, clist->get); } return req; } int mdss_dsi_cmdlist_put(struct mdss_dsi_ctrl_pdata *ctrl, struct dcs_cmd_req *cmdreq) { struct dcs_cmd_req *req; struct dcs_cmd_list *clist; int ret = 0; mutex_lock(&ctrl->cmd_mutex); clist = &ctrl->cmdlist; req = &clist->list[clist->put]; *req = *cmdreq; clist->put++; clist->put %= CMD_REQ_MAX; clist->tot++; if (clist->put == clist->get) { /* drop the oldest one */ pr_debug("%s: DROP, tot=%d put=%d get=%d\n", __func__, clist->tot, clist->put, clist->get); clist->get++; clist->get %= CMD_REQ_MAX; clist->tot--; } mutex_unlock(&ctrl->cmd_mutex); ret++; pr_debug("%s: tot=%d put=%d get=%d\n", __func__, clist->tot, clist->put, clist->get); if (req->flags & CMD_REQ_COMMIT) mdss_dsi_cmdlist_commit(ctrl, 0); return ret; } void mdss_dsi_ack_err_status(unsigned char *base) { u32 status; status = MIPI_INP(base + 0x0068);/* DSI_ACK_ERR_STATUS */ if (status) { MIPI_OUTP(base + 0x0068, status); pr_debug("%s: status=%x\n", __func__, status); } } void mdss_dsi_timeout_status(unsigned char *base) { u32 status; status = MIPI_INP(base + 0x00c0);/* DSI_TIMEOUT_STATUS */ if (status & 0x0111) { MIPI_OUTP(base + 0x00c0, status); pr_debug("%s: status=%x\n", __func__, status); } } void mdss_dsi_dln0_phy_err(unsigned char *base) { u32 status; status = MIPI_INP(base + 0x00b4);/* DSI_DLN0_PHY_ERR */ if (status & 0x011111) { MIPI_OUTP(base + 0x00b4, status); pr_debug("%s: status=%x\n", __func__, status); } } void mdss_dsi_fifo_status(unsigned char *base) { u32 status; status = MIPI_INP(base + 0x000c);/* DSI_FIFO_STATUS */ if (status & 0x44444489) { MIPI_OUTP(base + 0x000c, status); pr_debug("%s: status=%x\n", __func__, status); } } void mdss_dsi_status(unsigned char *base) { u32 status; status = MIPI_INP(base + 0x0008);/* DSI_STATUS */ if (status & 0x80000000) { MIPI_OUTP(base + 0x0008, status); pr_debug("%s: status=%x\n", __func__, status); } } void mdss_dsi_error(struct mdss_dsi_ctrl_pdata *ctrl) { unsigned char *base; base = ctrl->ctrl_base; /* DSI_ERR_INT_MASK0 */ mdss_dsi_ack_err_status(base); /* mask0, 0x01f */ mdss_dsi_timeout_status(base); /* mask0, 0x0e0 */ mdss_dsi_fifo_status(base); /* mask0, 0x133d00 */ mdss_dsi_status(base); /* mask0, 0xc0100 */ mdss_dsi_dln0_phy_err(base); /* mask0, 0x3e00000 */ } irqreturn_t mdss_dsi_isr(int irq, void *ptr) { u32 isr; struct mdss_dsi_ctrl_pdata *ctrl = (struct mdss_dsi_ctrl_pdata *)ptr; if (!ctrl->ctrl_base) pr_err("%s:%d DSI base adr no Initialized", __func__, __LINE__); isr = MIPI_INP(ctrl->ctrl_base + 0x0110);/* DSI_INTR_CTRL */ MIPI_OUTP(ctrl->ctrl_base + 0x0110, isr); if (ctrl->shared_pdata.broadcast_enable) if ((ctrl->panel_data.panel_info.pdest == DISPLAY_2) && (left_ctrl_pdata != NULL)) { u32 isr0; isr0 = MIPI_INP(left_ctrl_pdata->ctrl_base + 0x0110);/* DSI_INTR_CTRL */ MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110, isr0); } pr_debug("%s: isr=%x", __func__, isr); if (isr & DSI_INTR_ERROR) { pr_err("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR); spin_lock(&ctrl->mdp_lock); ctrl->mdp_busy = false; mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM); complete(&ctrl->mdp_comp); mdss_dsi_error(ctrl); spin_unlock(&ctrl->mdp_lock); } if (isr & DSI_INTR_VIDEO_DONE) { spin_lock(&ctrl->mdp_lock); mdss_dsi_disable_irq_nosync(ctrl, DSI_VIDEO_TERM); complete(&ctrl->video_comp); spin_unlock(&ctrl->mdp_lock); } if (isr & DSI_INTR_CMD_DMA_DONE) { spin_lock(&ctrl->mdp_lock); mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM); complete(&ctrl->dma_comp); spin_unlock(&ctrl->mdp_lock); } if (isr & DSI_INTR_CMD_MDP_DONE) { spin_lock(&ctrl->mdp_lock); ctrl->mdp_busy = false; mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM); complete(&ctrl->mdp_comp); spin_unlock(&ctrl->mdp_lock); } return IRQ_HANDLED; }