void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl) { struct mdss_dsi_ctrl_pdata *other_ctrl; if (ctrl == NULL) { pr_err("%s: Invalid input data\n", __func__); return; } ctrl->shared_ctrl_data->phy_disable_refcount++; /* * In dual-dsi configuration, the phy should be disabled for the * first controller only when the second controller is disabled. * This is true regardless of whether broadcast mode is enabled. */ if (!mdss_dsi_split_display_enabled() || ctrl->shared_ctrl_data->phy_disable_refcount == 2) { other_ctrl = mdss_dsi_get_other_ctrl(ctrl); if (other_ctrl) MIPI_OUTP(other_ctrl->phy_io.base + MDSS_DSI_DSIPHY_CTRL_0, 0x000); MIPI_OUTP(ctrl->phy_io.base + MDSS_DSI_DSIPHY_CTRL_0, 0x000); mdss_dsi_phy_regulator_disable(ctrl); /* * Wait for the registers writes to complete in order to * ensure that the phy is completely disabled */ wmb(); ctrl->shared_ctrl_data->phy_disable_refcount = 0; } }
/* * 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) { int ret = 0; struct mdss_dsi_ctrl_pdata *mctrl = NULL; /* * Turn on cmd mode in order to transmit the commands. * For video mode, do not send cmds more than one pixel line, * since it only transmit it during BLLP. */ if (mdss_dsi_sync_wait_enable(ctrl)) { if (mdss_dsi_sync_wait_trigger(ctrl)) { mctrl = mdss_dsi_get_other_ctrl(ctrl); if (!mctrl) { pr_warn("%s: sync_wait, NULL at other control\n", __func__); goto do_send; } mctrl->cmd_cfg_restore = __mdss_dsi_cmd_mode_config(mctrl, 1); } else if (!ctrl->do_unicast) { /* broadcast cmds, let cmd_trigger do it */ return 0; } } pr_debug("%s: ctrl=%d do_unicast=%d\n", __func__, ctrl->ndx, ctrl->do_unicast); do_send: ctrl->cmd_cfg_restore = __mdss_dsi_cmd_mode_config(ctrl, 1); ret = mdss_dsi_cmds2buf_tx(ctrl, cmds, cnt); if (IS_ERR_VALUE(ret)) { pr_err("%s: failed to call\n", __func__); cnt = -EINVAL; } if (!ctrl->do_unicast) { if (mctrl && mctrl->cmd_cfg_restore) { __mdss_dsi_cmd_mode_config(mctrl, 0); mctrl->cmd_cfg_restore = false; } if (ctrl->cmd_cfg_restore) { __mdss_dsi_cmd_mode_config(ctrl, 0); ctrl->cmd_cfg_restore = false; } } return cnt; }
int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type, int enable) { int rc = 0; int link_changed = 0, bus_changed = 0; int m_link_changed = 0, m_bus_changed = 0; struct mdss_dsi_ctrl_pdata *mctrl = NULL; if (!ctrl) { pr_err("%s: Invalid arg\n", __func__); return -EINVAL; } /* * In sync_wait_broadcast mode, we need to enable clocks * for the other controller as well when enabling clocks * for the trigger controller. * * If sync wait_broadcase mode is not enabled, but if split display * mode is enabled where both DSI controller's branch clocks are * sourced out of a single PLL, then we need to ensure that the * controller associated with that PLL also has it's clocks turned * on. This is required to make sure that if that controller's PLL/PHY * are clamped then they can be removed. */ if (mdss_dsi_sync_wait_trigger(ctrl)) { mctrl = mdss_dsi_get_other_ctrl(ctrl); if (!mctrl) pr_warn("%s: Unable to get other control\n", __func__); } else if (mdss_dsi_is_ctrl_clk_slave(ctrl)) { mctrl = mdss_dsi_get_ctrl_clk_master(); if (!mctrl) pr_warn("%s: Unable to get clk master control\n", __func__); } pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d\n", __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt, ctrl->link_clk_cnt); pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d, enable=%d\n", __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1, mctrl ? mctrl->link_clk_cnt : -1, enable); mutex_lock(&dsi_clk_lock); if (clk_type & DSI_BUS_CLKS) { bus_changed = __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, enable); if (bus_changed && mctrl) m_bus_changed = __mdss_dsi_update_clk_cnt( &mctrl->bus_clk_cnt, enable); } if (clk_type & DSI_LINK_CLKS) { link_changed = __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, enable); if (link_changed && mctrl) m_link_changed = __mdss_dsi_update_clk_cnt( &mctrl->link_clk_cnt, enable); } if (!link_changed && !bus_changed) goto no_error; /* clk cnts updated, nothing else needed */ /* * If updating link clock, need to make sure that the bus * clocks are enabled */ if (link_changed && (!bus_changed && !ctrl->bus_clk_cnt)) { pr_err("%s: Trying to enable link clks w/o enabling bus clks for ctrl%d", __func__, ctrl->ndx); goto error_mctrl_bus_start; } if (m_link_changed && (!m_bus_changed && !mctrl->bus_clk_cnt)) { pr_err("%s: Trying to enable link clks w/o enabling bus clks for ctrl%d", __func__, ctrl->ndx); goto error_mctrl_bus_start; } if (enable && m_bus_changed) { rc = mdss_dsi_clk_ctrl_sub(mctrl, DSI_BUS_CLKS, 1); if (rc) { pr_err("Failed to start mctrl bus clocks rc=%d\n", rc); goto error_mctrl_bus_start; } } if (enable && bus_changed) { rc = mdss_dsi_clk_ctrl_sub(ctrl, DSI_BUS_CLKS, 1); if (rc) { pr_err("Failed to start ctrl bus clocks rc=%d\n", rc); goto error_ctrl_bus_start; } } if (m_link_changed) { rc = mdss_dsi_clk_ctrl_sub(mctrl, DSI_LINK_CLKS, enable); if (rc) { pr_err("Failed to %s mctrl clocks. rc=%d\n", (enable ? "start" : "stop"), rc); goto error_mctrl_link_change; } } if (link_changed) { rc = mdss_dsi_clk_ctrl_sub(ctrl, DSI_LINK_CLKS, enable); if (rc) { pr_err("Failed to %s ctrl clocks. rc=%d\n", (enable ? "start" : "stop"), rc); goto error_ctrl_link_change; } } if (!enable && m_bus_changed) { rc = mdss_dsi_clk_ctrl_sub(mctrl, DSI_BUS_CLKS, 0); if (rc) { pr_err("Failed to stop mctrl bus clocks rc=%d\n", rc); goto error_mctrl_bus_stop; } } if (!enable && bus_changed) { rc = mdss_dsi_clk_ctrl_sub(ctrl, DSI_BUS_CLKS, 0); if (rc) { pr_err("Failed to stop ctrl bus clocks\n rc=%d", rc); goto error_ctrl_bus_stop; } } goto no_error; error_ctrl_bus_stop: if (m_bus_changed) mdss_dsi_clk_ctrl_sub(mctrl, DSI_BUS_CLKS, 1); error_mctrl_bus_stop: if (link_changed) mdss_dsi_clk_ctrl_sub(ctrl, DSI_LINK_CLKS, enable ? 0 : 1); error_ctrl_link_change: if (m_link_changed) mdss_dsi_clk_ctrl_sub(mctrl, DSI_LINK_CLKS, enable ? 0 : 1); error_mctrl_link_change: if (bus_changed && enable) mdss_dsi_clk_ctrl_sub(ctrl, DSI_BUS_CLKS, 0); error_ctrl_bus_start: if (m_bus_changed && enable) mdss_dsi_clk_ctrl_sub(mctrl, DSI_BUS_CLKS, 0); error_mctrl_bus_start: if (clk_type & DSI_BUS_CLKS) { if (mctrl) __mdss_dsi_update_clk_cnt(&mctrl->bus_clk_cnt, enable ? 0 : 1); __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, enable ? 0 : 1); } if (clk_type & DSI_LINK_CLKS) { if (mctrl) __mdss_dsi_update_clk_cnt(&mctrl->link_clk_cnt, enable ? 0 : 1); __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, enable ? 0 : 1); } no_error: mutex_unlock(&dsi_clk_lock); pr_debug("%s--: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d changed=%d\n", __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt, ctrl->link_clk_cnt, link_changed && bus_changed); pr_debug("%s--: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d, m_changed=%d, enable=%d\n", __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1, mctrl ? mctrl->link_clk_cnt : -1, m_link_changed && m_bus_changed, enable); return rc; }
/* * mdss_dsi_cmds_rx() - dcs read from panel * @ctrl: dsi controller * @cmds: read command descriptor * @len: number of bytes to read back * * controller have 4 registers can hold 16 bytes of rxed data * dcs packet: 4 bytes header + payload + 2 bytes crc * 2 padding bytes add to payload to have payload length is mutipled by 4 * 1st read: 4 bytes header + 8 bytes payload + 2 padding + 2 crc * 2nd read: 12 bytes payload + 2 padding + 2 crc * 3rd read: 12 bytes payload + 2 padding + 2 crc * */ int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_cmd_desc *cmds, int rlen) { int data_byte, rx_byte, dlen, end; int short_response, diff, pkt_size, ret = 0; struct dsi_buf *tp, *rp; char cmd; struct mdss_dsi_ctrl_pdata *mctrl = NULL; /* * Turn on cmd mode in order to transmit the commands. * For video mode, do not send cmds more than one pixel line, * since it only transmit it during BLLP. */ if (mdss_dsi_sync_wait_enable(ctrl)) { if (mdss_dsi_sync_wait_trigger(ctrl)) { mctrl = mdss_dsi_get_other_ctrl(ctrl); if (!mctrl) { pr_warn("%s: sync_wait, NULL at other control\n", __func__); goto do_send; } mctrl->cmd_cfg_restore = __mdss_dsi_cmd_mode_config(mctrl, 1); } else { /* skip cmds, let cmd_trigger do it */ return 0; } } do_send: ctrl->cmd_cfg_restore = __mdss_dsi_cmd_mode_config(ctrl, 1); if (rlen <= 2) { short_response = 1; pkt_size = rlen; rx_byte = 4; } else { short_response = 0; data_byte = 8; /* first read */ /* * add extra 2 padding bytes 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. */ pkt_size = data_byte + 2; rx_byte = data_byte + 8; /* 4 header + 2 crc + 2 padding*/ } tp = &ctrl->tx_buf; rp = &ctrl->rx_buf; end = 0; mdss_dsi_buf_init(rp); while (!end) { pr_debug("%s: rlen=%d pkt_size=%d rx_byte=%d\n", __func__, rlen, pkt_size, rx_byte); max_pktsize[0] = pkt_size; mdss_dsi_buf_init(tp); ret = mdss_dsi_cmd_dma_add(tp, &pkt_size_cmd); if (!ret) { pr_err("%s: failed to add max_pkt_size\n", __func__); rp->len = 0; goto end; } mdss_dsi_wait4video_eng_busy(ctrl); mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM); ret = mdss_dsi_cmd_dma_tx(ctrl, tp); if (IS_ERR_VALUE(ret)) { mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM); pr_err("%s: failed to tx max_pkt_size\n", __func__); rp->len = 0; goto end; } pr_debug("%s: max_pkt_size=%d sent\n", __func__, pkt_size); mdss_dsi_buf_init(tp); ret = mdss_dsi_cmd_dma_add(tp, cmds); if (!ret) { pr_err("%s: failed to add cmd = 0x%x\n", __func__, cmds->payload[0]); rp->len = 0; goto end; } mdss_dsi_wait4video_eng_busy(ctrl); /* video mode only */ mdss_dsi_enable_irq(ctrl, DSI_CMD_TERM); /* transmit read comamnd to client */ ret = mdss_dsi_cmd_dma_tx(ctrl, tp); if (IS_ERR_VALUE(ret)) { mdss_dsi_disable_irq(ctrl, DSI_CMD_TERM); pr_err("%s: failed to tx cmd = 0x%x\n", __func__, cmds->payload[0]); rp->len = 0; goto end; } /* * once cmd_dma_done interrupt received, * return data from client is ready and stored * at RDBK_DATA register already * since rx fifo is 16 bytes, dcs header is kept at first loop, * after that dcs header lost during shift into registers */ dlen = mdss_dsi_cmd_dma_rx(ctrl, rp, rx_byte); if (short_response) break; if (rlen <= data_byte) { diff = data_byte - rlen; end = 1; } else { diff = 0; rlen -= data_byte; } dlen -= 2; /* 2 padding bytes */ dlen -= 2; /* 2 crc */ dlen -= diff; rp->data += dlen; /* next start position */ rp->len += dlen; data_byte = 12; /* NOT first read */ pkt_size += data_byte; pr_debug("%s: rp data=%x len=%d dlen=%d diff=%d\n", __func__, (int)rp->data, rp->len, dlen, diff); } rp->data = rp->start; /* move back to start position */ cmd = rp->data[0]; switch (cmd) { case DTYPE_ACK_ERR_RESP: pr_debug("%s: rx ACK_ERR_PACLAGE\n", __func__); rp->len = 0; 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: pr_warning("%s:Invalid response cmd\n", __func__); rp->len = 0; } end: if (mctrl && mctrl->cmd_cfg_restore) { __mdss_dsi_cmd_mode_config(mctrl, 0); mctrl->cmd_cfg_restore = false; } if (ctrl->cmd_cfg_restore) { __mdss_dsi_cmd_mode_config(ctrl, 0); ctrl->cmd_cfg_restore = false; } return rp->len; }
static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_buf *tp) { int len, ret = 0; int domain = MDSS_IOMMU_DOMAIN_UNSECURE; char *bp; struct mdss_dsi_ctrl_pdata *mctrl = NULL; bp = tp->data; len = ALIGN(tp->len, 4); ctrl->dma_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, ctrl->dma_size, SZ_4K, 0, &(ctrl->dma_addr)); if (IS_ERR_VALUE(ret)) { pr_err("unable to map dma memory to iommu(%d)\n", ret); return -ENOMEM; } } else { ctrl->dma_addr = tp->dmap; } INIT_COMPLETION(ctrl->dma_comp); if (mdss_dsi_sync_wait_trigger(ctrl)) { /* broadcast same cmd to other panel */ mctrl = mdss_dsi_get_other_ctrl(ctrl); if (mctrl && mctrl->dma_addr == 0) { MIPI_OUTP(mctrl->ctrl_base + 0x048, ctrl->dma_addr); MIPI_OUTP(mctrl->ctrl_base + 0x04c, len); MIPI_OUTP(mctrl->ctrl_base + 0x090, 0x01); /* trigger */ } } /* send cmd to its panel */ MIPI_OUTP((ctrl->ctrl_base) + 0x048, ctrl->dma_addr); MIPI_OUTP((ctrl->ctrl_base) + 0x04c, len); wmb(); MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); wmb(); if (ctrl->do_unicast) { /* let cmd_trigger to kickoff later */ pr_debug("%s: SKIP, ndx=%d do_unicast=%d\n", __func__, ctrl->ndx, ctrl->do_unicast); ret = tp->len; goto end; } ret = wait_for_completion_timeout(&ctrl->dma_comp, msecs_to_jiffies(DMA_TX_TIMEOUT)); if (ret == 0) ret = -ETIMEDOUT; else ret = tp->len; if (mctrl && mctrl->dma_addr) { if (is_mdss_iommu_attached()) { msm_iommu_unmap_contig_buffer(mctrl->dma_addr, mdss_get_iommu_domain(domain), 0, mctrl->dma_size); } mctrl->dma_addr = 0; mctrl->dma_size = 0; } if (is_mdss_iommu_attached()) { msm_iommu_unmap_contig_buffer(ctrl->dma_addr, mdss_get_iommu_domain(domain), 0, ctrl->dma_size); } ctrl->dma_addr = 0; ctrl->dma_size = 0; end: return ret; }