Created
March 28, 2015 15:20
-
-
Save invisiblek/96b4cc1235b4eeed08e7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig | |
index ebc014a..01edf92 100644 | |
--- a/drivers/video/msm/mdss/Kconfig | |
+++ b/drivers/video/msm/mdss/Kconfig | |
@@ -35,7 +35,3 @@ config FB_MSM_MDSS_MDP3 | |
---help--- | |
The MDP3 provides support for an older version display controller | |
included in latest display sub-system, known as MDSS. | |
- | |
-config LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- bool "MIPI LGD NT35521 WXGA PANEL" | |
- default n | |
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile | |
index fd03b63..4eb1033 100644 | |
--- a/drivers/video/msm/mdss/Makefile | |
+++ b/drivers/video/msm/mdss/Makefile | |
@@ -1,25 +1,26 @@ | |
-mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o | |
+mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o dsi_status_v2.o | |
mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o | |
obj-$(CONFIG_FB_MSM_MDSS_MDP3) += mdss-mdp3.o | |
-mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o | |
+mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o dsi_status_6g.o | |
mdss-mdp-objs += mdss_mdp_pp.o | |
mdss-mdp-objs += mdss_mdp_intf_video.o | |
mdss-mdp-objs += mdss_mdp_intf_cmd.o | |
mdss-mdp-objs += mdss_mdp_intf_writeback.o | |
mdss-mdp-objs += mdss_mdp_rotator.o | |
mdss-mdp-objs += mdss_mdp_overlay.o | |
+mdss-mdp-objs += mdss_mdp_splash_logo.o | |
mdss-mdp-objs += mdss_mdp_wb.o | |
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o | |
ifeq ($(CONFIG_FB_MSM_MDSS),y) | |
-obj-$(CONFIG_DEBUG_FS) += mdss_debug.o | |
+obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o | |
endif | |
dsi-v2-objs = dsi_v2.o dsi_host_v2.o dsi_io_v2.o | |
obj-$(CONFIG_FB_MSM_MDSS_MDP3) += dsi-v2.o | |
-mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o | |
+mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o | |
mdss-dsi-objs += mdss_dsi_panel.o | |
mdss-dsi-objs += msm_mdss_io_8974.o | |
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o | |
@@ -42,8 +43,4 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o | |
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o | |
-ifeq ($(CONFIG_FB_MSM_MDSS_MDP3),y) | |
-obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += dsi_status_v2.o | |
-else | |
obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += mdss_dsi_status.o | |
-endif | |
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c | |
index 53b6fc6..68c991c 100644 | |
--- a/drivers/video/msm/mdss/dsi_host_v2.c | |
+++ b/drivers/video/msm/mdss/dsi_host_v2.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -26,6 +26,7 @@ | |
#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 | |
@@ -203,6 +204,13 @@ irqreturn_t msm_dsi_isr_handler(int irq, void *ptr) | |
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); | |
@@ -213,22 +221,20 @@ irqreturn_t msm_dsi_isr_handler(int irq, void *ptr) | |
msm_dsi_error(dsi_host_private->dsi_base); | |
} | |
- spin_lock(&ctrl->mdp_lock); | |
- | |
if (isr & DSI_INTR_VIDEO_DONE) | |
complete(&ctrl->video_comp); | |
if (isr & DSI_INTR_CMD_DMA_DONE) | |
complete(&ctrl->dma_comp); | |
- spin_unlock(&ctrl->mdp_lock); | |
- | |
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; | |
} | |
@@ -236,6 +242,13 @@ int msm_dsi_irq_init(struct device *dev, int irq_no, | |
struct mdss_dsi_ctrl_pdata *ctrl) | |
{ | |
int ret; | |
+ u32 isr; | |
+ | |
+ 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); | |
@@ -524,8 +537,11 @@ void msm_dsi_controller_cfg(int enable) | |
if (readl_poll_timeout((ctrl_base + DSI_STATUS), | |
status, | |
((status & 0x02) == 0), | |
- DSI_POLL_SLEEP_US, DSI_POLL_TIMEOUT_US)) | |
+ 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), | |
@@ -562,15 +578,20 @@ void msm_dsi_op_mode_config(int mode, struct mdss_panel_data *pdata) | |
pr_debug("msm_dsi_op_mode_config\n"); | |
dsi_ctrl = MIPI_INP(ctrl_base + DSI_CTRL); | |
- /*If Video enabled, Keep Video and Cmd mode ON */ | |
- | |
- dsi_ctrl &= ~0x06; | |
- | |
- if (mode == DSI_VIDEO_MODE) | |
- dsi_ctrl |= 0x02; | |
+ if (dsi_ctrl & DSI_VIDEO_MODE_EN) | |
+ dsi_ctrl &= ~(DSI_CMD_MODE_EN|DSI_EN); | |
else | |
- dsi_ctrl |= 0x04; | |
+ 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 { /* command mode */ | |
+ 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); | |
@@ -632,14 +653,25 @@ int msm_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
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, | |
+}; | |
+ | |
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; | |
+ u32 *lp, data, *temp; | |
+ int i, j = 0, off, cnt; | |
unsigned char *ctrl_base = dsi_host_private->dsi_base; | |
+ char reg[16]; | |
+ int repeated_bytes = 0; | |
lp = (u32 *)rp->data; | |
+ temp = (u32 *)reg; | |
cnt = rlen; | |
cnt += 3; | |
cnt >>= 2; | |
@@ -647,16 +679,52 @@ int msm_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, | |
if (cnt > 4) | |
cnt = 4; /* 4 x 32 bits registers only */ | |
+ if (rlen == 4) | |
+ rp->read_cnt = 4; | |
+ else | |
+ rp->read_cnt = (max_pktsize[0] + 6); | |
+ | |
+ if (rp->read_cnt > 16) { | |
+ int bytes_shifted, data_lost = 0, rem_header_bytes = 0; | |
+ /* Any data more than 16 bytes will be shifted out */ | |
+ bytes_shifted = rp->read_cnt - rlen; | |
+ if (bytes_shifted >= 4) | |
+ data_lost = bytes_shifted - 4; /* remove dcs header */ | |
+ else | |
+ rem_header_bytes = 4 - bytes_shifted; /* rem header */ | |
+ /* | |
+ * (rp->len - 4) -> current rx buffer data length. | |
+ * If data_lost > 0, then ((rp->len - 4) - data_lost) will be | |
+ * the number of repeating bytes. | |
+ * If data_lost == 0, then ((rp->len - 4) + rem_header_bytes) | |
+ * will be the number of bytes repeating in between rx buffer | |
+ * and the current RDBK_DATA registers. We need to skip the | |
+ * repeating bytes. | |
+ */ | |
+ repeated_bytes = (rp->len - 4) - data_lost + rem_header_bytes; | |
+ } | |
+ | |
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 */ | |
+ /* to network byte order */ | |
+ if (!repeated_bytes) | |
+ *lp++ = ntohl(data); | |
+ else | |
+ *temp++ = ntohl(data); | |
pr_debug("%s: data = 0x%x and ntohl(data) = 0x%x\n", | |
__func__, data, ntohl(data)); | |
off -= 4; | |
- rp->len += sizeof(*lp); | |
+ if (rlen == 4) | |
+ rp->len += sizeof(*lp); | |
+ } | |
+ | |
+ /* Skip duplicates and append other data to the rx buffer */ | |
+ if (repeated_bytes) { | |
+ for (i = repeated_bytes; i < 16; i++) | |
+ rp->data[j++] = reg[i]; | |
} | |
return rlen; | |
@@ -748,14 +816,6 @@ static int msm_dsi_parse_rx_response(struct dsi_buf *rp) | |
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) | |
{ | |
@@ -793,10 +853,23 @@ static int msm_dsi_cmds_rx_1(struct mdss_dsi_ctrl_pdata *ctrl, | |
{ | |
int rc; | |
struct dsi_buf *tp, *rp; | |
+ int rx_byte = 0; | |
+ | |
+ if (rlen <= 2) | |
+ rx_byte = 4; | |
+ else | |
+ rx_byte = DSI_MAX_BYTES_TO_READ; | |
tp = &ctrl->tx_buf; | |
rp = &ctrl->rx_buf; | |
mdss_dsi_buf_init(rp); | |
+ rc = msm_dsi_set_max_packet_size(ctrl, rlen); | |
+ if (rc) { | |
+ pr_err("%s: dsi_set_max_pkt failed\n", __func__); | |
+ rc = -EINVAL; | |
+ goto dsi_cmds_rx_1_error; | |
+ } | |
+ | |
mdss_dsi_buf_init(tp); | |
rc = mdss_dsi_cmd_dma_add(tp, cmds); | |
@@ -819,10 +892,12 @@ static int msm_dsi_cmds_rx_1(struct mdss_dsi_ctrl_pdata *ctrl, | |
} | |
if (rlen <= DSI_SHORT_PKT_DATA_SIZE) { | |
- msm_dsi_cmd_dma_rx(ctrl, rp, rlen); | |
+ msm_dsi_cmd_dma_rx(ctrl, rp, rx_byte); | |
} else { | |
- msm_dsi_cmd_dma_rx(ctrl, rp, rlen + DSI_HOST_HDR_SIZE); | |
- rp->len = rlen + DSI_HOST_HDR_SIZE; | |
+ msm_dsi_cmd_dma_rx(ctrl, rp, rx_byte); | |
+ rp->len = rx_byte - 2; /*2 bytes for CRC*/ | |
+ rp->len = rp->len - (DSI_MAX_PKT_SIZE - rlen); | |
+ rp->data = rp->start + (16 - (rlen + 2 + DSI_HOST_HDR_SIZE)); | |
} | |
rc = msm_dsi_parse_rx_response(rp); | |
@@ -839,16 +914,15 @@ static int msm_dsi_cmds_rx_2(struct mdss_dsi_ctrl_pdata *ctrl, | |
{ | |
int rc; | |
struct dsi_buf *tp, *rp; | |
- int pkt_size, data_bytes, total; | |
+ int pkt_size, data_bytes, dlen, end = 0, diff; | |
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) { | |
+ while (!end) { | |
rc = msm_dsi_set_max_packet_size(ctrl, pkt_size); | |
if (rc) | |
break; | |
@@ -859,7 +933,7 @@ static int msm_dsi_cmds_rx_2(struct mdss_dsi_ctrl_pdata *ctrl, | |
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__); | |
@@ -873,19 +947,32 @@ static int msm_dsi_cmds_rx_2(struct mdss_dsi_ctrl_pdata *ctrl, | |
} | |
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 (rlen <= data_bytes) { | |
+ diff = data_bytes - rlen; | |
+ end = 1; | |
+ } else { | |
+ diff = 0; | |
+ rlen -= data_bytes; | |
+ } | |
+ dlen = DSI_MAX_BYTES_TO_READ - 2; | |
+ dlen -= diff; | |
+ rp->data += dlen; | |
+ rp->len += dlen; | |
+ | |
+ if (!end) { | |
+ data_bytes = 14; | |
+ if (rlen < data_bytes) | |
+ pkt_size += rlen; | |
+ else | |
+ pkt_size += data_bytes; | |
+ } | |
+ pr_debug("%s: rp data=%x len=%d dlen=%d diff=%d\n", | |
+ __func__, (int) (unsigned long) rp->data, | |
+ rp->len, dlen, diff); | |
} | |
if (!rc) { | |
rp->data = rp->start; | |
- rp->len = rlen + DSI_HOST_HDR_SIZE; | |
rc = msm_dsi_parse_rx_response(rp); | |
} | |
@@ -956,7 +1043,13 @@ int msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) | |
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)) | |
@@ -971,6 +1064,7 @@ int msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) | |
dsi_set_tx_power_mode(1); | |
msm_dsi_clk_ctrl(&ctrl->panel_data, 0); | |
+ mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P); | |
mutex_unlock(&ctrl->cmd_mutex); | |
return 0; | |
@@ -1047,13 +1141,15 @@ static int msm_dsi_on(struct mdss_panel_data *pdata) | |
mutex_lock(&ctrl_pdata->mutex); | |
- ret = msm_dss_enable_vreg( | |
- ctrl_pdata->power_data.vreg_config, | |
- ctrl_pdata->power_data.num_vreg, 1); | |
- if (ret) { | |
- pr_err("%s: DSI power on failed\n", __func__); | |
- mutex_unlock(&ctrl_pdata->mutex); | |
- return ret; | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ ret = msm_dss_enable_vreg( | |
+ ctrl_pdata->power_data.vreg_config, | |
+ ctrl_pdata->power_data.num_vreg, 1); | |
+ if (ret) { | |
+ pr_err("%s: DSI power on failed\n", __func__); | |
+ mutex_unlock(&ctrl_pdata->mutex); | |
+ return ret; | |
+ } | |
} | |
msm_dsi_ahb_ctrl(1); | |
@@ -1171,11 +1267,13 @@ static int msm_dsi_off(struct mdss_panel_data *pdata) | |
msm_dsi_phy_off(dsi_host_private->dsi_base); | |
msm_dsi_ahb_ctrl(0); | |
- ret = msm_dss_enable_vreg( | |
- ctrl_pdata->power_data.vreg_config, | |
- ctrl_pdata->power_data.num_vreg, 0); | |
- if (ret) { | |
- pr_err("%s: Panel power off failed\n", __func__); | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ ret = msm_dss_enable_vreg( | |
+ ctrl_pdata->power_data.vreg_config, | |
+ ctrl_pdata->power_data.num_vreg, 0); | |
+ if (ret) { | |
+ pr_err("%s: Panel power off failed\n", __func__); | |
+ } | |
} | |
dsi_host_private->clk_count = 0; | |
dsi_host_private->dsi_on = 0; | |
@@ -1213,6 +1311,13 @@ static int msm_dsi_cont_on(struct mdss_panel_data *pdata) | |
mutex_unlock(&ctrl_pdata->mutex); | |
return ret; | |
} | |
+ pinfo->panel_power_on = 1; | |
+ 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(); | |
@@ -1224,7 +1329,83 @@ static int msm_dsi_cont_on(struct mdss_panel_data *pdata) | |
return 0; | |
} | |
-int msm_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+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 (ctrl_pdata->status_buf.data[0] != | |
+ ctrl_pdata->status_value) { | |
+ 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; | |
@@ -1409,13 +1590,16 @@ static int msm_dsi_clk_ctrl(struct mdss_panel_data *pdata, int enable) | |
&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); | |
} | |
} | |
@@ -1436,9 +1620,19 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) | |
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; | |
- ctrl->check_status = msm_dsi_bta_status_check; | |
+ | |
+ 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 int __devinit msm_dsi_probe(struct platform_device *pdev) | |
@@ -1500,14 +1694,6 @@ static int __devinit msm_dsi_probe(struct platform_device *pdev) | |
__func__, __LINE__); | |
rc = -ENODEV; | |
goto error_irq_resource; | |
- } else { | |
- 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_resource; | |
- } | |
} | |
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); | |
@@ -1570,6 +1756,14 @@ static int __devinit msm_dsi_probe(struct platform_device *pdev) | |
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_device_register; | |
+ } | |
+ | |
rc = dsi_panel_device_register_v2(pdev, ctrl_pdata); | |
if (rc) { | |
pr_err("%s: dsi panel dev reg failed\n", __func__); | |
diff --git a/drivers/video/msm/mdss/dsi_host_v2.h b/drivers/video/msm/mdss/dsi_host_v2.h | |
index b297452..991a769 100644 | |
--- a/drivers/video/msm/mdss/dsi_host_v2.h | |
+++ b/drivers/video/msm/mdss/dsi_host_v2.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -25,6 +25,7 @@ | |
#define DSI_INTR_CMD_MDP_DONE BIT(8) | |
#define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) | |
#define DSI_INTR_CMD_DMA_DONE BIT(0) | |
+#define DSI_INTR_ALL_MASK 0x2220202 | |
#define DSI_BTA_TERM BIT(1) | |
@@ -170,4 +171,8 @@ | |
#define DSI_DSIPHY_BIST_CTRL4 0x049C | |
#define DSI_DSIPHY_BIST_CTRL5 0x04A0 | |
+#define DSI_EN BIT(0) | |
+#define DSI_VIDEO_MODE_EN BIT(1) | |
+#define DSI_CMD_MODE_EN BIT(2) | |
+ | |
#endif /* DSI_HOST_V2_H */ | |
diff --git a/drivers/video/msm/mdss/dsi_status_v2.c b/drivers/video/msm/mdss/dsi_status_v2.c | |
index d62ddf3..f0966cc 100644 | |
--- a/drivers/video/msm/mdss/dsi_status_v2.c | |
+++ b/drivers/video/msm/mdss/dsi_status_v2.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2013-2014, 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 | |
@@ -10,49 +10,26 @@ | |
* GNU General Public License for more details. | |
*/ | |
-#include <linux/module.h> | |
-#include <linux/kernel.h> | |
-#include <linux/init.h> | |
-#include <linux/fb.h> | |
-#include <linux/notifier.h> | |
#include <linux/workqueue.h> | |
#include <linux/delay.h> | |
-#include <linux/debugfs.h> | |
-#include <linux/slab.h> | |
-#include <linux/uaccess.h> | |
-#include <linux/iopoll.h> | |
#include <linux/kobject.h> | |
#include <linux/string.h> | |
#include <linux/sysfs.h> | |
-#include "mdss_fb.h" | |
#include "mdss_dsi.h" | |
-#include "mdss_panel.h" | |
#include "mdp3_ctrl.h" | |
-#define STATUS_CHECK_INTERVAL 5000 | |
- | |
-/** | |
- * dsi_status_data - Stores all the data necessary for this module | |
- * @fb_notif: Used to egister for the fb events | |
- * @live_status: Delayed worker structure, used to associate the | |
- * delayed worker function | |
- * @mfd: Used to store the msm_fb_data_type received when the notifier | |
- * call back happens | |
- * @root: Stores the dir created by debuugfs | |
- * @debugfs_reset_panel: The debugfs variable used to inject errors | |
+/* | |
+ * mdp3_check_dsi_ctrl_status() - Check MDP3 DSI controller status periodically. | |
+ * @work : dsi controller status data | |
+ * @interval : duration in milliseconds to schedule work queue | |
+ * | |
+ * This function calls check_status API on DSI controller to send the BTA | |
+ * command. If DSI controller fails to acknowledge the BTA command, it sends | |
+ * the PANEL_ALIVE=0 status to HAL layer. | |
*/ | |
- | |
-struct dsi_status_data { | |
- struct notifier_block fb_notifier; | |
- struct delayed_work check_status; | |
- struct msm_fb_data_type *mfd; | |
- uint32_t check_interval; | |
-}; | |
-struct dsi_status_data *pstatus_data; | |
-static uint32_t interval = STATUS_CHECK_INTERVAL; | |
- | |
-void check_dsi_ctrl_status(struct work_struct *work) | |
+void mdp3_check_dsi_ctrl_status(struct work_struct *work, | |
+ uint32_t interval) | |
{ | |
struct dsi_status_data *pdsi_status = NULL; | |
struct mdss_panel_data *pdata = NULL; | |
@@ -61,9 +38,10 @@ void check_dsi_ctrl_status(struct work_struct *work) | |
int ret = 0; | |
pdsi_status = container_of(to_delayed_work(work), | |
- struct dsi_status_data, check_status); | |
- if (!pdsi_status) { | |
- pr_err("%s: DSI status data not available\n", __func__); | |
+ struct dsi_status_data, check_status); | |
+ | |
+ if (!pdsi_status || !(pdsi_status->mfd)) { | |
+ pr_err("%s: mfd not available\n", __func__); | |
return; | |
} | |
@@ -72,117 +50,51 @@ void check_dsi_ctrl_status(struct work_struct *work) | |
pr_err("%s: Panel data not available\n", __func__); | |
return; | |
} | |
+ | |
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
panel_data); | |
+ | |
if (!ctrl_pdata || !ctrl_pdata->check_status) { | |
- pr_err("%s: DSI ctrl or status_check callback not avilable\n", | |
+ pr_err("%s: DSI ctrl or status_check callback not available\n", | |
__func__); | |
return; | |
} | |
+ | |
mdp3_session = pdsi_status->mfd->mdp.private1; | |
+ if (!mdp3_session) { | |
+ pr_err("%s: Display is off\n", __func__); | |
+ return; | |
+ } | |
+ | |
mutex_lock(&mdp3_session->lock); | |
+ if (!mdp3_session->status) { | |
+ pr_info("display off already\n"); | |
+ mutex_unlock(&mdp3_session->lock); | |
+ return; | |
+ } | |
- ret = ctrl_pdata->check_status(ctrl_pdata); | |
+ if (mdp3_session->wait_for_dma_done) | |
+ ret = mdp3_session->wait_for_dma_done(mdp3_session); | |
+ if (!ret) | |
+ ret = ctrl_pdata->check_status(ctrl_pdata); | |
+ else | |
+ pr_err("%s: wait_for_dma_done error\n", __func__); | |
mutex_unlock(&mdp3_session->lock); | |
if ((pdsi_status->mfd->panel_power_on)) { | |
if (ret > 0) { | |
schedule_delayed_work(&pdsi_status->check_status, | |
- msecs_to_jiffies(pdsi_status->check_interval)); | |
+ msecs_to_jiffies(interval)); | |
} else { | |
char *envp[2] = {"PANEL_ALIVE=0", NULL}; | |
pdata->panel_info.panel_dead = true; | |
ret = kobject_uevent_env( | |
- &pdsi_status->mfd->fbi->dev->kobj, | |
- KOBJ_CHANGE, envp); | |
+ &pdsi_status->mfd->fbi->dev->kobj, | |
+ KOBJ_CHANGE, envp); | |
pr_err("%s: Panel has gone bad, sending uevent - %s\n", | |
__func__, envp[0]); | |
} | |
} | |
} | |
-/** | |
- * fb_notifier_callback() - Call back function for the fb_register_client() | |
- * notifying events | |
- * @self : notifier block | |
- * @event : The event that was triggered | |
- * @data : Of type struct fb_event | |
- * | |
- * - This function listens for FB_BLANK_UNBLANK and FB_BLANK_POWERDOWN events | |
- * - Based on the event the delayed work is either scheduled again after | |
- * PANEL_STATUS_CHECK_INTERVAL or cancelled | |
- */ | |
-static int fb_event_callback(struct notifier_block *self, | |
- unsigned long event, void *data) | |
-{ | |
- struct fb_event *evdata = (struct fb_event *)data; | |
- struct dsi_status_data *pdata = container_of(self, | |
- struct dsi_status_data, fb_notifier); | |
- pdata->mfd = (struct msm_fb_data_type *)evdata->info->par; | |
- | |
- if (event == FB_EVENT_BLANK && evdata) { | |
- int *blank = evdata->data; | |
- switch (*blank) { | |
- case FB_BLANK_UNBLANK: | |
- schedule_delayed_work(&pdata->check_status, | |
- msecs_to_jiffies(STATUS_CHECK_INTERVAL)); | |
- break; | |
- case FB_BLANK_POWERDOWN: | |
- cancel_delayed_work(&pdata->check_status); | |
- break; | |
- } | |
- } | |
- return 0; | |
-} | |
- | |
-int __init mdss_dsi_status_init(void) | |
-{ | |
- int rc; | |
- | |
- pstatus_data = kzalloc(sizeof(struct dsi_status_data), GFP_KERNEL); | |
- if (!pstatus_data) { | |
- pr_err("%s: can't alloc mem\n", __func__); | |
- rc = -ENOMEM; | |
- return rc; | |
- } | |
- | |
- memset(pstatus_data, 0, sizeof(struct dsi_status_data)); | |
- | |
- pstatus_data->fb_notifier.notifier_call = fb_event_callback; | |
- | |
- rc = fb_register_client(&pstatus_data->fb_notifier); | |
- if (rc < 0) { | |
- pr_err("%s: fb_register_client failed, returned with rc=%d\n", | |
- __func__, rc); | |
- kfree(pstatus_data); | |
- return -EPERM; | |
- } | |
- | |
- pstatus_data->check_interval = interval; | |
- pr_info("%s: DSI status check interval:%d\n", __func__, interval); | |
- | |
- INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status); | |
- | |
- pr_debug("%s: DSI ctrl status thread initialized\n", __func__); | |
- | |
- return rc; | |
-} | |
- | |
-void __exit mdss_dsi_status_exit(void) | |
-{ | |
- fb_unregister_client(&pstatus_data->fb_notifier); | |
- cancel_delayed_work_sync(&pstatus_data->check_status); | |
- kfree(pstatus_data); | |
- pr_debug("%s: DSI ctrl status thread removed\n", __func__); | |
-} | |
- | |
-module_param(interval, uint, 0); | |
-MODULE_PARM_DESC(interval, | |
- "Duration in milliseconds to send BTA command for checking" | |
- "DSI status periodically"); | |
- | |
-module_init(mdss_dsi_status_init); | |
-module_exit(mdss_dsi_status_exit); | |
- | |
-MODULE_LICENSE("GPL v2"); | |
diff --git a/drivers/video/msm/mdss/dsi_v2.c b/drivers/video/msm/mdss/dsi_v2.c | |
index ccde545..4fc3909 100644 | |
--- a/drivers/video/msm/mdss/dsi_v2.c | |
+++ b/drivers/video/msm/mdss/dsi_v2.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -52,6 +52,37 @@ static int dsi_on(struct mdss_panel_data *pdata) | |
return rc; | |
} | |
+static int dsi_update_pconfig(struct mdss_panel_data *pdata, | |
+ int mode) | |
+{ | |
+ int ret = 0; | |
+ struct mdss_panel_info *pinfo = &pdata->panel_info; | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ if (!pdata) | |
+ return -ENODEV; | |
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
+ panel_data); | |
+ | |
+ if (mode == DSI_CMD_MODE) { | |
+ pinfo->mipi.mode = DSI_CMD_MODE; | |
+ pinfo->type = MIPI_CMD_PANEL; | |
+ pinfo->mipi.vsync_enable = 1; | |
+ pinfo->mipi.hw_vsync_mode = 1; | |
+ } else { | |
+ pinfo->mipi.mode = DSI_VIDEO_MODE; | |
+ pinfo->type = MIPI_VIDEO_PANEL; | |
+ pinfo->mipi.vsync_enable = 0; | |
+ pinfo->mipi.hw_vsync_mode = 0; | |
+ } | |
+ | |
+ ctrl_pdata->panel_mode = pinfo->mipi.mode; | |
+ mdss_panel_get_dst_fmt(pinfo->bpp, pinfo->mipi.mode, | |
+ pinfo->mipi.pixel_packing, &(pinfo->mipi.dst_format)); | |
+ pinfo->cont_splash_enabled = 0; | |
+ | |
+ return ret; | |
+} | |
+ | |
static int dsi_panel_handler(struct mdss_panel_data *pdata, int enable) | |
{ | |
int rc = 0; | |
@@ -64,17 +95,47 @@ static int dsi_panel_handler(struct mdss_panel_data *pdata, int enable) | |
panel_data); | |
if (enable) { | |
- dsi_ctrl_gpio_request(ctrl_pdata); | |
- mdss_dsi_panel_reset(pdata, 1); | |
- rc = ctrl_pdata->on(pdata); | |
- if (rc) | |
- pr_err("dsi_panel_handler panel on failed %d\n", rc); | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ if (pdata->panel_info.type == MIPI_CMD_PANEL) | |
+ dsi_ctrl_gpio_request(ctrl_pdata); | |
+ mdss_dsi_panel_reset(pdata, 1); | |
+ } | |
+ pdata->panel_info.panel_power_on = 1; | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ rc = ctrl_pdata->on(pdata); | |
+ if (rc) | |
+ pr_err("%s: panel on failed!\n", __func__); | |
+ } | |
+ if (pdata->panel_info.type == MIPI_CMD_PANEL && | |
+ pdata->panel_info.dynamic_switch_pending) { | |
+ dsi_ctrl_gpio_request(ctrl_pdata); | |
+ mdss_dsi_set_tear_on(ctrl_pdata); | |
+ } | |
} else { | |
+ msm_dsi_sw_reset(); | |
if (dsi_intf.op_mode_config) | |
dsi_intf.op_mode_config(DSI_CMD_MODE, pdata); | |
- rc = ctrl_pdata->off(pdata); | |
- mdss_dsi_panel_reset(pdata, 0); | |
- dsi_ctrl_gpio_free(ctrl_pdata); | |
+ if (pdata->panel_info.dynamic_switch_pending) { | |
+ pr_info("%s: switching to %s mode\n", __func__, | |
+ (pdata->panel_info.mipi.mode ? "video" : "command")); | |
+ if (pdata->panel_info.type == MIPI_CMD_PANEL) { | |
+ ctrl_pdata->switch_mode(pdata, DSI_VIDEO_MODE); | |
+ dsi_ctrl_gpio_free(ctrl_pdata); | |
+ } else if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { | |
+ ctrl_pdata->switch_mode(pdata, DSI_CMD_MODE); | |
+ dsi_ctrl_gpio_request(ctrl_pdata); | |
+ mdss_dsi_set_tear_off(ctrl_pdata); | |
+ dsi_ctrl_gpio_free(ctrl_pdata); | |
+ } | |
+ } | |
+ if (!pdata->panel_info.dynamic_switch_pending) | |
+ rc = ctrl_pdata->off(pdata); | |
+ pdata->panel_info.panel_power_on = 0; | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ if (pdata->panel_info.type == MIPI_CMD_PANEL) | |
+ dsi_ctrl_gpio_free(ctrl_pdata); | |
+ mdss_dsi_panel_reset(pdata, 0); | |
+ } | |
} | |
return rc; | |
} | |
@@ -136,6 +197,9 @@ static int dsi_event_handler(struct mdss_panel_data *pdata, | |
case MDSS_EVENT_PANEL_CLK_CTRL: | |
rc = dsi_clk_ctrl(pdata, (int)arg); | |
break; | |
+ case MDSS_EVENT_DSI_DYNAMIC_SWITCH: | |
+ rc = dsi_update_pconfig(pdata, (int)(unsigned long) arg); | |
+ break; | |
default: | |
pr_debug("%s: unhandled event=%d\n", __func__, event); | |
break; | |
@@ -156,7 +220,8 @@ static int dsi_parse_gpio(struct platform_device *pdev, | |
__func__, __LINE__); | |
ctrl_pdata->disp_te_gpio = -1; | |
- if (ctrl_pdata->panel_data.panel_info.mipi.mode == DSI_CMD_MODE) { | |
+ if (ctrl_pdata->panel_data.panel_info.mipi.mode == DSI_CMD_MODE || | |
+ ctrl_pdata->panel_data.panel_info.mipi.dynamic_switch_enabled) { | |
ctrl_pdata->disp_te_gpio = of_get_named_gpio(np, | |
"qcom,platform-te-gpio", 0); | |
if (!gpio_is_valid(ctrl_pdata->disp_te_gpio)) | |
@@ -202,75 +267,23 @@ int dsi_ctrl_gpio_request(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
int rc = 0; | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { | |
- rc = gpio_request(ctrl_pdata->disp_en_gpio, "disp_enable"); | |
- if (rc) | |
- goto gpio_request_err4; | |
- | |
- ctrl_pdata->disp_en_gpio_requested = 1; | |
- } | |
- | |
- if (gpio_is_valid(ctrl_pdata->rst_gpio)) { | |
- rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); | |
- if (rc) | |
- goto gpio_request_err3; | |
- | |
- ctrl_pdata->rst_gpio_requested = 1; | |
- } | |
- | |
if (gpio_is_valid(ctrl_pdata->disp_te_gpio)) { | |
rc = gpio_request(ctrl_pdata->disp_te_gpio, "disp_te"); | |
if (rc) | |
- goto gpio_request_err2; | |
- | |
- ctrl_pdata->disp_te_gpio_requested = 1; | |
+ ctrl_pdata->disp_te_gpio_requested = 0; | |
+ else | |
+ ctrl_pdata->disp_te_gpio_requested = 1; | |
} | |
- if (gpio_is_valid(ctrl_pdata->mode_gpio)) { | |
- rc = gpio_request(ctrl_pdata->mode_gpio, "panel_mode"); | |
- if (rc) | |
- goto gpio_request_err1; | |
- | |
- ctrl_pdata->mode_gpio_requested = 1; | |
- } | |
- | |
- return rc; | |
- | |
-gpio_request_err1: | |
- if (gpio_is_valid(ctrl_pdata->disp_te_gpio)) | |
- gpio_free(ctrl_pdata->disp_te_gpio); | |
-gpio_request_err2: | |
- if (gpio_is_valid(ctrl_pdata->rst_gpio)) | |
- gpio_free(ctrl_pdata->rst_gpio); | |
-gpio_request_err3: | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
- gpio_free(ctrl_pdata->disp_en_gpio); | |
-gpio_request_err4: | |
- ctrl_pdata->disp_en_gpio_requested = 0; | |
- ctrl_pdata->rst_gpio_requested = 0; | |
- ctrl_pdata->disp_te_gpio_requested = 0; | |
- ctrl_pdata->mode_gpio_requested = 0; | |
return rc; | |
} | |
void dsi_ctrl_gpio_free(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
- if (ctrl_pdata->disp_en_gpio_requested) { | |
- gpio_free(ctrl_pdata->disp_en_gpio); | |
- ctrl_pdata->disp_en_gpio_requested = 0; | |
- } | |
- if (ctrl_pdata->rst_gpio_requested) { | |
- gpio_free(ctrl_pdata->rst_gpio); | |
- ctrl_pdata->rst_gpio_requested = 0; | |
- } | |
if (ctrl_pdata->disp_te_gpio_requested) { | |
gpio_free(ctrl_pdata->disp_te_gpio); | |
ctrl_pdata->disp_te_gpio_requested = 0; | |
} | |
- if (ctrl_pdata->mode_gpio_requested) { | |
- gpio_free(ctrl_pdata->mode_gpio); | |
- ctrl_pdata->mode_gpio_requested = 0; | |
- } | |
} | |
static int dsi_parse_vreg(struct device *dev, struct dss_module_power *mp) | |
@@ -283,7 +296,7 @@ static int dsi_parse_vreg(struct device *dev, struct dss_module_power *mp) | |
if (!dev || !mp) { | |
pr_err("%s: invalid input\n", __func__); | |
rc = -EINVAL; | |
- goto error; | |
+ return rc; | |
} | |
np = dev->of_node; | |
diff --git a/drivers/video/msm/mdss/dsi_v2.h b/drivers/video/msm/mdss/dsi_v2.h | |
index b8c91da..1474570 100644 | |
--- a/drivers/video/msm/mdss/dsi_v2.h | |
+++ b/drivers/video/msm/mdss/dsi_v2.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -56,4 +56,5 @@ struct mdss_panel_cfg *mdp3_panel_intf_type(int intf_val); | |
int mdp3_panel_get_boot_cfg(void); | |
+void msm_dsi_sw_reset(void); | |
#endif /* DSI_V2_H */ | |
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c | |
index 0cf1093..d4d913f 100644 | |
--- a/drivers/video/msm/mdss/mdp3.c | |
+++ b/drivers/video/msm/mdss/mdp3.c | |
@@ -47,6 +47,7 @@ | |
#include <mach/iommu.h> | |
#include <mach/iommu_domains.h> | |
#include <mach/msm_memtypes.h> | |
+#include <mach/rpm-regulator-smd.h> | |
#include "mdp3.h" | |
#include "mdss_fb.h" | |
@@ -189,11 +190,11 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr) | |
u32 mdp_status = 0; | |
spin_lock(&mdata->irq_lock); | |
- if (!mdata->irq_mask) | |
+ if (!mdata->irq_mask) { | |
pr_err("spurious interrupt\n"); | |
- | |
- clk_enable(mdp3_res->clocks[MDP3_CLK_AHB]); | |
- clk_enable(mdp3_res->clocks[MDP3_CLK_CORE]); | |
+ spin_unlock(&mdata->irq_lock); | |
+ return IRQ_HANDLED; | |
+ } | |
mdp_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS); | |
mdp_interrupt = mdp_status; | |
@@ -209,9 +210,6 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr) | |
} | |
MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_status); | |
- clk_disable(mdp3_res->clocks[MDP3_CLK_AHB]); | |
- clk_disable(mdp3_res->clocks[MDP3_CLK_CORE]); | |
- | |
spin_unlock(&mdata->irq_lock); | |
return IRQ_HANDLED; | |
@@ -279,19 +277,51 @@ void mdp3_irq_register(void) | |
pr_debug("mdp3_irq_register\n"); | |
spin_lock_irqsave(&mdp3_res->irq_lock, flag); | |
- enable_irq(mdp3_res->irq); | |
+ mdp3_res->irq_ref_cnt++; | |
+ if (mdp3_res->irq_ref_cnt == 1) { | |
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask); | |
+ enable_irq(mdp3_res->irq); | |
+ } | |
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); | |
} | |
void mdp3_irq_deregister(void) | |
{ | |
unsigned long flag; | |
+ bool irq_enabled = true; | |
pr_debug("mdp3_irq_deregister\n"); | |
spin_lock_irqsave(&mdp3_res->irq_lock, flag); | |
memset(mdp3_res->irq_ref_count, 0, sizeof(u32) * MDP3_MAX_INTR); | |
mdp3_res->irq_mask = 0; | |
- disable_irq_nosync(mdp3_res->irq); | |
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0); | |
+ mdp3_res->irq_ref_cnt--; | |
+ /* This can happen if suspend is called first */ | |
+ if (mdp3_res->irq_ref_cnt < 0) { | |
+ irq_enabled = false; | |
+ mdp3_res->irq_ref_cnt = 0; | |
+ } | |
+ if (mdp3_res->irq_ref_cnt == 0 && irq_enabled) | |
+ disable_irq_nosync(mdp3_res->irq); | |
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); | |
+} | |
+ | |
+void mdp3_irq_suspend(void) | |
+{ | |
+ unsigned long flag; | |
+ bool irq_enabled = true; | |
+ | |
+ pr_debug("%s\n", __func__); | |
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag); | |
+ mdp3_res->irq_ref_cnt--; | |
+ if (mdp3_res->irq_ref_cnt < 0) { | |
+ irq_enabled = false; | |
+ mdp3_res->irq_ref_cnt = 0; | |
+ } | |
+ if (mdp3_res->irq_ref_cnt == 0 && irq_enabled) { | |
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, 0); | |
+ disable_irq_nosync(mdp3_res->irq); | |
+ } | |
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag); | |
} | |
@@ -401,6 +431,12 @@ int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota) | |
} | |
bus_handle->current_bus_idx = bus_idx; | |
rc = msm_bus_scale_client_update_request(bus_handle->handle, bus_idx); | |
+ | |
+ if (!rc && ab_quota != 0 && ib_quota != 0) { | |
+ bus_handle->restore_ab = ab_quota; | |
+ bus_handle->restore_ib = ib_quota; | |
+ } | |
+ | |
return rc; | |
} | |
@@ -423,10 +459,18 @@ static int mdp3_clk_update(u32 clk_idx, u32 enable) | |
count = mdp3_res->clock_ref_count[clk_idx]; | |
if (count == 1 && enable) { | |
pr_debug("clk=%d en=%d\n", clk_idx, enable); | |
+ ret = clk_prepare(clk); | |
+ if (ret) { | |
+ pr_err("%s: Failed to prepare clock %d", | |
+ __func__, clk_idx); | |
+ mdp3_res->clock_ref_count[clk_idx]--; | |
+ return ret; | |
+ } | |
ret = clk_enable(clk); | |
} else if (count == 0) { | |
pr_debug("clk=%d disable\n", clk_idx); | |
clk_disable(clk); | |
+ clk_unprepare(clk); | |
ret = 0; | |
} else if (count < 0) { | |
pr_err("clk=%d count=%d\n", clk_idx, count); | |
@@ -578,54 +622,76 @@ int mdp3_clk_enable(int enable, int dsi_clk) | |
return rc; | |
} | |
-int mdp3_clk_prepare(void) | |
+void mdp3_bus_bw_iommu_enable(int enable, int client) | |
{ | |
- int rc = 0; | |
+ struct mdp3_bus_handle_map *bus_handle; | |
+ int client_idx; | |
+ u64 ab, ib; | |
+ int ref_cnt; | |
- mutex_lock(&mdp3_res->res_mutex); | |
- mdp3_res->clk_prepare_count++; | |
- if (mdp3_res->clk_prepare_count == 1) { | |
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_AHB]); | |
- if (rc < 0) | |
- goto error0; | |
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_CORE]); | |
- if (rc < 0) | |
- goto error1; | |
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_VSYNC]); | |
- if (rc < 0) | |
- goto error2; | |
- rc = clk_prepare(mdp3_res->clocks[MDP3_CLK_DSI]); | |
- if (rc < 0) | |
- goto error3; | |
+ if (client == MDP3_CLIENT_DMA_P) { | |
+ client_idx = MDP3_BUS_HANDLE_DMA; | |
+ } else if (client == MDP3_CLIENT_PPP) { | |
+ client_idx = MDP3_BUS_HANDLE_PPP; | |
+ } else { | |
+ pr_err("invalid client %d\n", client); | |
+ return; | |
} | |
- mutex_unlock(&mdp3_res->res_mutex); | |
- return rc; | |
-error3: | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_VSYNC]); | |
-error2: | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_CORE]); | |
-error1: | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_AHB]); | |
-error0: | |
- mdp3_res->clk_prepare_count--; | |
+ bus_handle = &mdp3_res->bus_handle[client_idx]; | |
+ if (bus_handle->handle < 1) { | |
+ pr_err("invalid bus handle %d\n", bus_handle->handle); | |
+ return; | |
+ } | |
+ mutex_lock(&mdp3_res->res_mutex); | |
+ if (enable) | |
+ bus_handle->ref_cnt++; | |
+ else | |
+ bus_handle->ref_cnt--; | |
+ ref_cnt = bus_handle->ref_cnt; | |
mutex_unlock(&mdp3_res->res_mutex); | |
- return rc; | |
+ | |
+ if (enable && ref_cnt == 1) { | |
+ if (mdp3_res->allow_iommu_update) | |
+ mdp3_iommu_enable(client); | |
+ ab = bus_handle->restore_ab; | |
+ ib = bus_handle->restore_ib; | |
+ mdp3_bus_scale_set_quota(client, ab, ib); | |
+ } else if (!enable && ref_cnt == 0) { | |
+ mdp3_bus_scale_set_quota(client, 0, 0); | |
+ mdp3_iommu_disable(client); | |
+ } else if (ref_cnt < 0) { | |
+ pr_err("Ref count < 0, bus client=%d, ref_cnt=%d", | |
+ client_idx, ref_cnt); | |
+ } | |
} | |
-void mdp3_clk_unprepare(void) | |
+int mdp3_res_update(int enable, int dsi_clk, int client) | |
{ | |
- mutex_lock(&mdp3_res->res_mutex); | |
- mdp3_res->clk_prepare_count--; | |
- if (mdp3_res->clk_prepare_count == 0) { | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_AHB]); | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_CORE]); | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_VSYNC]); | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_DSI]); | |
- } else if (mdp3_res->clk_prepare_count < 0) { | |
- pr_err("mdp3 clk unprepare mismatch\n"); | |
+ int rc = 0; | |
+ | |
+ if (enable) { | |
+ rc = mdp3_clk_enable(enable, dsi_clk); | |
+ if (rc < 0) { | |
+ pr_err("mdp3_clk_enable failed, enable=%d, dsi_clk=%d\n", | |
+ enable, dsi_clk); | |
+ goto done; | |
+ } | |
+ mdp3_irq_register(); | |
+ mdp3_bus_bw_iommu_enable(enable, client); | |
+ } else { | |
+ mdp3_bus_bw_iommu_enable(enable, client); | |
+ mdp3_irq_suspend(); | |
+ rc = mdp3_clk_enable(enable, dsi_clk); | |
+ if (rc < 0) { | |
+ pr_err("mdp3_clk_enable failed, enable=%d, dsi_clk=%d\n", | |
+ enable, dsi_clk); | |
+ goto done; | |
+ } | |
} | |
- mutex_unlock(&mdp3_res->res_mutex); | |
+ | |
+done: | |
+ return rc; | |
} | |
int mdp3_get_mdp_dsi_clk(void) | |
@@ -633,7 +699,6 @@ int mdp3_get_mdp_dsi_clk(void) | |
int rc; | |
mutex_lock(&mdp3_res->res_mutex); | |
- clk_prepare(mdp3_res->clocks[MDP3_CLK_DSI]); | |
rc = mdp3_clk_update(MDP3_CLK_DSI, 1); | |
mutex_unlock(&mdp3_res->res_mutex); | |
return rc; | |
@@ -644,7 +709,6 @@ int mdp3_put_mdp_dsi_clk(void) | |
int rc; | |
mutex_lock(&mdp3_res->res_mutex); | |
rc = mdp3_clk_update(MDP3_CLK_DSI, 0); | |
- clk_unprepare(mdp3_res->clocks[MDP3_CLK_DSI]); | |
mutex_unlock(&mdp3_res->res_mutex); | |
return rc; | |
} | |
@@ -674,9 +738,11 @@ int mdp3_iommu_attach(int context) | |
if (context >= MDP3_IOMMU_CTX_MAX) | |
return -EINVAL; | |
+ mutex_lock(&mdp3_res->iommu_lock); | |
context_map = mdp3_res->iommu_contexts + context; | |
if (context_map->attached) { | |
pr_warn("mdp iommu already attached\n"); | |
+ mutex_unlock(&mdp3_res->iommu_lock); | |
return 0; | |
} | |
@@ -685,6 +751,7 @@ int mdp3_iommu_attach(int context) | |
iommu_attach_device(domain_map->domain, context_map->ctx); | |
context_map->attached = true; | |
+ mutex_unlock(&mdp3_res->iommu_lock); | |
return 0; | |
} | |
@@ -697,9 +764,11 @@ int mdp3_iommu_dettach(int context) | |
context >= MDP3_IOMMU_CTX_MAX) | |
return -EINVAL; | |
+ mutex_lock(&mdp3_res->iommu_lock); | |
context_map = mdp3_res->iommu_contexts + context; | |
if (!context_map->attached) { | |
pr_warn("mdp iommu not attached\n"); | |
+ mutex_unlock(&mdp3_res->iommu_lock); | |
return 0; | |
} | |
@@ -707,6 +776,7 @@ int mdp3_iommu_dettach(int context) | |
iommu_detach_device(domain_map->domain, context_map->ctx); | |
context_map->attached = false; | |
+ mutex_unlock(&mdp3_res->iommu_lock); | |
return 0; | |
} | |
@@ -1141,6 +1211,54 @@ static int mdp3_parse_dt(struct platform_device *pdev) | |
return 0; | |
} | |
+void msm_mdp3_cx_ctrl(int enable) | |
+{ | |
+ int rc; | |
+ | |
+ if (!mdp3_res->vdd_cx) { | |
+ mdp3_res->vdd_cx = devm_regulator_get(&mdp3_res->pdev->dev, | |
+ "vdd-cx"); | |
+ if (IS_ERR_OR_NULL(mdp3_res->vdd_cx)) { | |
+ pr_debug("unable to get CX reg. rc=%d\n", | |
+ PTR_RET(mdp3_res->vdd_cx)); | |
+ mdp3_res->vdd_cx = NULL; | |
+ return; | |
+ } | |
+ } | |
+ | |
+ if (enable) { | |
+ rc = regulator_set_voltage( | |
+ mdp3_res->vdd_cx, | |
+ RPM_REGULATOR_CORNER_SVS_SOC, | |
+ RPM_REGULATOR_CORNER_SUPER_TURBO); | |
+ if (rc < 0) | |
+ goto vreg_set_voltage_fail; | |
+ | |
+ rc = regulator_enable(mdp3_res->vdd_cx); | |
+ if (rc) { | |
+ pr_err("Failed to enable regulator vdd_cx.\n"); | |
+ return; | |
+ } | |
+ } else { | |
+ rc = regulator_disable(mdp3_res->vdd_cx); | |
+ if (rc) { | |
+ pr_err("Failed to disable regulator vdd_cx.\n"); | |
+ return; | |
+ } | |
+ rc = regulator_set_voltage( | |
+ mdp3_res->vdd_cx, | |
+ RPM_REGULATOR_CORNER_NONE, | |
+ RPM_REGULATOR_CORNER_SUPER_TURBO); | |
+ if (rc < 0) | |
+ goto vreg_set_voltage_fail; | |
+ } | |
+ | |
+ return; | |
+vreg_set_voltage_fail: | |
+ pr_err("Set vltg failed\n"); | |
+ return; | |
+} | |
+ | |
void mdp3_batfet_ctrl(int enable) | |
{ | |
int rc; | |
@@ -1173,6 +1291,12 @@ void mdp3_batfet_ctrl(int enable) | |
pr_err("%s: reg enable/disable failed", __func__); | |
} | |
+void mdp3_enable_regulator(int enable) | |
+{ | |
+ msm_mdp3_cx_ctrl(enable); | |
+ mdp3_batfet_ctrl(enable); | |
+} | |
+ | |
static void mdp3_iommu_heap_unmap_iommu(struct mdp3_iommu_meta *meta) | |
{ | |
unsigned int domain_num; | |
@@ -1849,7 +1973,9 @@ static int mdp3_is_display_on(struct mdss_panel_data *pdata) | |
static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) | |
{ | |
struct mdss_panel_info *panel_info = &pdata->panel_info; | |
- int ab, ib, rc; | |
+ struct mdp3_bus_handle_map *bus_handle; | |
+ u64 ab, ib; | |
+ int rc; | |
pr_debug("mdp3__continuous_splash_on\n"); | |
@@ -1859,26 +1985,23 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) | |
mdp3_clk_set_rate(MDP3_CLK_CORE, MDP_CORE_CLK_RATE, | |
MDP3_CLIENT_DMA_P); | |
- rc = mdp3_clk_prepare(); | |
- if (rc) { | |
- pr_err("fail to prepare clk\n"); | |
- return rc; | |
- } | |
- | |
- rc = mdp3_clk_enable(1, 1); | |
- if (rc) { | |
- pr_err("fail to enable clk\n"); | |
- mdp3_clk_unprepare(); | |
- return rc; | |
+ bus_handle = &mdp3_res->bus_handle[MDP3_BUS_HANDLE_DMA]; | |
+ if (bus_handle->handle < 1) { | |
+ pr_err("invalid bus handle %d\n", bus_handle->handle); | |
+ return -EINVAL; | |
} | |
ab = panel_info->xres * panel_info->yres * 4; | |
ab *= panel_info->mipi.frame_rate; | |
ib = (ab * 3) / 2; | |
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib); | |
+ bus_handle->restore_ab = ab; | |
+ bus_handle->restore_ib = ib; | |
+ | |
+ rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P); | |
if (rc) { | |
- pr_err("fail to request bus bandwidth\n"); | |
- goto splash_on_err; | |
+ pr_err("fail to enable clk\n"); | |
+ return rc; | |
} | |
rc = mdp3_ppp_init(); | |
@@ -1887,8 +2010,6 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) | |
goto splash_on_err; | |
} | |
- mdp3_irq_register(); | |
- | |
if (pdata->event_handler) { | |
rc = pdata->event_handler(pdata, MDSS_EVENT_CONT_SPLASH_BEGIN, | |
NULL); | |
@@ -1903,15 +2024,14 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata) | |
else | |
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_CMD].active = 1; | |
- mdp3_batfet_ctrl(true); | |
+ mdp3_enable_regulator(true); | |
mdp3_res->cont_splash_en = 1; | |
return 0; | |
splash_on_err: | |
- if (mdp3_clk_enable(0, 1)) | |
+ if (mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P)) | |
pr_err("%s: Unable to disable mdp3 clocks\n", __func__); | |
- mdp3_clk_unprepare(); | |
return rc; | |
} | |
@@ -1931,6 +2051,12 @@ static int mdp3_panel_register_done(struct mdss_panel_data *pdata) | |
rc = mdp3_continuous_splash_on(pdata); | |
} | |
} | |
+ /* | |
+ * We want to prevent iommu from being enabled if there is | |
+ * continue splash screen. This would have happened in | |
+ * res_update in continuous_splash_on without this flag. | |
+ */ | |
+ mdp3_res->allow_iommu_update = true; | |
return rc; | |
} | |
@@ -1945,11 +2071,9 @@ static int mdp3_debug_dump_stats(void *data, char *buf, int len) | |
static void mdp3_debug_enable_clock(int on) | |
{ | |
if (on) { | |
- mdp3_clk_prepare(); | |
mdp3_clk_enable(1, 0); | |
} else { | |
mdp3_clk_enable(0, 0); | |
- mdp3_clk_unprepare(); | |
} | |
} | |
@@ -2148,6 +2272,7 @@ static int mdp3_probe(struct platform_device *pdev) | |
.panel_register_done = mdp3_panel_register_done, | |
.fb_stride = mdp3_fb_stride, | |
.fb_mem_alloc_fnc = mdp3_alloc, | |
+ .check_dsi_status = mdp3_check_dsi_ctrl_status, | |
}; | |
struct mdp3_intr_cb underrun_cb = { | |
@@ -2248,7 +2373,7 @@ int mdp3_panel_get_boot_cfg(void) | |
if (!mdp3_res || !mdp3_res->pan_cfg.init_done) | |
rc = -EPROBE_DEFER; | |
- if (mdp3_res->pan_cfg.lk_cfg) | |
+ else if (mdp3_res->pan_cfg.lk_cfg) | |
rc = 1; | |
else | |
rc = 0; | |
@@ -2257,13 +2382,13 @@ int mdp3_panel_get_boot_cfg(void) | |
static int mdp3_suspend_sub(struct mdp3_hw_resource *mdata) | |
{ | |
- mdp3_batfet_ctrl(false); | |
+ mdp3_enable_regulator(false); | |
return 0; | |
} | |
static int mdp3_resume_sub(struct mdp3_hw_resource *mdata) | |
{ | |
- mdp3_batfet_ctrl(true); | |
+ mdp3_enable_regulator(true); | |
return 0; | |
} | |
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h | |
index a253d8f..137a1b8 100644 | |
--- a/drivers/video/msm/mdss/mdp3.h | |
+++ b/drivers/video/msm/mdss/mdp3.h | |
@@ -74,6 +74,9 @@ struct mdp3_bus_handle_map { | |
struct msm_bus_paths *usecases; | |
struct msm_bus_scale_pdata *scale_pdata; | |
int current_bus_idx; | |
+ int ref_cnt; | |
+ u64 restore_ab; | |
+ u64 restore_ib; | |
u32 handle; | |
}; | |
@@ -134,6 +137,7 @@ struct mdp3_hw_resource { | |
struct ion_client *ion_client; | |
struct mdp3_iommu_domain_map *domains; | |
struct mdp3_iommu_ctx_map *iommu_contexts; | |
+ bool allow_iommu_update; | |
struct ion_handle *ion_handle; | |
struct mutex iommu_lock; | |
struct rb_root iommu_root; | |
@@ -144,6 +148,7 @@ struct mdp3_hw_resource { | |
spinlock_t irq_lock; | |
u32 irq_ref_count[MDP3_MAX_INTR]; | |
u32 irq_mask; | |
+ int irq_ref_cnt; | |
struct mdp3_intr_cb callbacks[MDP3_MAX_INTR]; | |
u32 underrun_cnt; | |
@@ -159,6 +164,7 @@ struct mdp3_hw_resource { | |
bool batfet_required; | |
struct regulator *batfet; | |
+ struct regulator *vdd_cx; | |
}; | |
struct mdp3_img_data { | |
@@ -183,8 +189,7 @@ void mdp3_irq_register(void); | |
void mdp3_irq_deregister(void); | |
int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate, int client); | |
int mdp3_clk_enable(int enable, int dsi_clk); | |
-int mdp3_clk_prepare(void); | |
-void mdp3_clk_unprepare(void); | |
+int mdp3_res_update(int enable, int dsi_clk, int client); | |
int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota); | |
int mdp3_put_img(struct mdp3_img_data *data, int client); | |
int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, | |
@@ -203,6 +208,9 @@ void mdp3_batfet_ctrl(int enable); | |
int mdp3_misr_set(struct mdp_misr *misr_req); | |
int mdp3_misr_get(struct mdp_misr *misr_resp); | |
+void mdp3_enable_regulator(int enable); | |
+void mdp3_check_dsi_ctrl_status(struct work_struct *work, | |
+ uint32_t interval); | |
#define MDP3_REG_WRITE(addr, val) writel_relaxed(val, mdp3_res->mdp_base + addr) | |
#define MDP3_REG_READ(addr) readl_relaxed(mdp3_res->mdp_base + addr) | |
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c | |
index 731e71e..27524af 100644 | |
--- a/drivers/video/msm/mdss/mdp3_ctrl.c | |
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c | |
@@ -49,7 +49,7 @@ static void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq) | |
if (!count) | |
return; | |
- while (count--) { | |
+ while (count-- && (bufq->pop_idx >= 0)) { | |
struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx]; | |
bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE; | |
mdp3_put_img(data, MDP3_CLIENT_DMA_P); | |
@@ -110,6 +110,7 @@ int mdp3_ctrl_notify(struct mdp3_session_data *ses, int event) | |
static void mdp3_dispatch_dma_done(struct work_struct *work) | |
{ | |
struct mdp3_session_data *session; | |
+ int cnt = 0; | |
pr_debug("%s\n", __func__); | |
session = container_of(work, struct mdp3_session_data, | |
@@ -117,7 +118,13 @@ static void mdp3_dispatch_dma_done(struct work_struct *work) | |
if (!session) | |
return; | |
- mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE); | |
+ cnt = atomic_read(&session->dma_done_cnt); | |
+ | |
+ while (cnt > 0) { | |
+ mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE); | |
+ atomic_dec(&session->dma_done_cnt); | |
+ cnt--; | |
+ } | |
} | |
static void mdp3_dispatch_clk_off(struct work_struct *work) | |
@@ -153,7 +160,9 @@ void vsync_notify_handler(void *arg) | |
void dma_done_notify_handler(void *arg) | |
{ | |
struct mdp3_session_data *session = (struct mdp3_session_data *)arg; | |
+ atomic_inc(&session->dma_done_cnt); | |
schedule_work(&session->dma_done_work); | |
+ complete(&session->dma_completion); | |
} | |
void vsync_count_down(void *arg) | |
@@ -293,7 +302,7 @@ static ssize_t mdp3_vsync_show_event(struct device *dev, | |
vsync_ticks = ktime_to_ns(mdp3_session->vsync_time); | |
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks); | |
- rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks); | |
+ rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks); | |
return rc; | |
} | |
@@ -326,7 +335,7 @@ static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable) | |
(!enable && session->clk_on == 1)) { | |
rc = panel->event_handler(panel, | |
MDSS_EVENT_PANEL_CLK_CTRL, (void *)enable); | |
- rc |= mdp3_clk_enable(enable, 1); | |
+ rc |= mdp3_res_update(enable, 1, MDP3_CLIENT_DMA_P); | |
} else { | |
pr_debug("enable = %d, clk_on=%d\n", enable, session->clk_on); | |
} | |
@@ -340,8 +349,8 @@ static int mdp3_ctrl_res_req_bus(struct msm_fb_data_type *mfd, int status) | |
int rc = 0; | |
if (status) { | |
struct mdss_panel_info *panel_info = mfd->panel_info; | |
- int ab = 0; | |
- int ib = 0; | |
+ u64 ab = 0; | |
+ u64 ib = 0; | |
ab = panel_info->xres * panel_info->yres * 4; | |
ab *= panel_info->mipi.frame_rate; | |
ib = (ab * 3) / 2; | |
@@ -362,24 +371,15 @@ static int mdp3_ctrl_res_req_clk(struct msm_fb_data_type *mfd, int status) | |
mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE, | |
MDP3_CLIENT_DMA_P); | |
- rc = mdp3_clk_prepare(); | |
- if (rc) { | |
- pr_err("mdp3 clk prepare fail\n"); | |
- return rc; | |
- } | |
- | |
- rc = mdp3_clk_enable(1, 1); | |
+ rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P); | |
if (rc) { | |
pr_err("mdp3 clk enable fail\n"); | |
- mdp3_clk_unprepare(); | |
return rc; | |
} | |
} else { | |
- rc = mdp3_clk_enable(0, 1); | |
+ rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P); | |
if (rc) | |
pr_err("mdp3 clk disable fail\n"); | |
- else | |
- mdp3_clk_unprepare(); | |
} | |
return rc; | |
} | |
@@ -423,10 +423,10 @@ static int mdp3_ctrl_get_source_format(u32 imgType) | |
return format; | |
} | |
-static int mdp3_ctrl_get_pack_pattern(struct msm_fb_data_type *mfd) | |
+static int mdp3_ctrl_get_pack_pattern(u32 imgType) | |
{ | |
int packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_RGB; | |
- if (mfd->fb_imgType == MDP_RGBA_8888) | |
+ if (imgType == MDP_RGBA_8888) | |
packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_BGR; | |
return packPattern; | |
} | |
@@ -474,6 +474,7 @@ static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd, | |
video->hsync_polarity = 1; | |
video->vsync_polarity = 1; | |
video->de_polarity = 1; | |
+ video->underflow_color = p->lcdc.underflow_clr; | |
} else if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { | |
cfg.dsi_cmd.primary_dsi_cmd_id = 0; | |
cfg.dsi_cmd.secondary_dsi_cmd_id = 1; | |
@@ -502,6 +503,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, | |
int vbp, vfp, vspw; | |
int vtotal, vporch; | |
struct mdp3_notification dma_done_callback; | |
+ struct mdp3_tear_check te; | |
vbp = panel_info->lcdc.v_back_porch; | |
vfp = panel_info->lcdc.v_front_porch; | |
@@ -527,18 +529,34 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd, | |
outputConfig.out_sel = mdp3_ctrl_get_intf_type(mfd); | |
outputConfig.bit_mask_polarity = 0; | |
outputConfig.color_components_flip = 0; | |
- outputConfig.pack_pattern = mdp3_ctrl_get_pack_pattern(mfd); | |
+ outputConfig.pack_pattern = mdp3_ctrl_get_pack_pattern(mfd->fb_imgType); | |
outputConfig.pack_align = MDP3_DMA_OUTPUT_PACK_ALIGN_LSB; | |
outputConfig.color_comp_out_bits = (MDP3_DMA_OUTPUT_COMP_BITS_8 << 4) | | |
(MDP3_DMA_OUTPUT_COMP_BITS_8 << 2)| | |
MDP3_DMA_OUTPUT_COMP_BITS_8; | |
+ te.frame_rate = panel_info->mipi.frame_rate; | |
+ te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode; | |
+ te.tear_check_en = panel_info->te.tear_check_en; | |
+ te.sync_cfg_height = panel_info->te.sync_cfg_height; | |
+ te.vsync_init_val = panel_info->te.vsync_init_val; | |
+ te.sync_threshold_start = panel_info->te.sync_threshold_start; | |
+ te.sync_threshold_continue = panel_info->te.sync_threshold_continue; | |
+ te.start_pos = panel_info->te.start_pos; | |
+ te.rd_ptr_irq = panel_info->te.rd_ptr_irq; | |
+ te.refx100 = panel_info->te.refx100; | |
+ | |
if (dma->dma_config) | |
rc = dma->dma_config(dma, &sourceConfig, &outputConfig); | |
else | |
rc = -EINVAL; | |
if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { | |
+ if (dma->dma_sync_config) | |
+ rc = dma->dma_sync_config(dma, | |
+ &sourceConfig, &te); | |
+ else | |
+ rc = -EINVAL; | |
dma_done_callback.handler = dma_done_notify_handler; | |
dma_done_callback.arg = mfd->mdp.private1; | |
dma->dma_done_notifier(dma, &dma_done_callback); | |
@@ -571,16 +589,10 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd) | |
goto on_error; | |
} | |
- mdp3_batfet_ctrl(true); | |
+ mdp3_enable_regulator(true); | |
mdp3_ctrl_notifier_register(mdp3_session, | |
&mdp3_session->mfd->mdp_sync_pt_data.notifier); | |
- rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P); | |
- if (rc) { | |
- pr_err("fail to attach MDP DMA SMMU\n"); | |
- goto on_error; | |
- } | |
- | |
/* request bus bandwidth before DSI DMA traffic */ | |
rc = mdp3_ctrl_res_req_bus(mfd, 1); | |
if (rc) { | |
@@ -603,8 +615,6 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd) | |
goto on_error; | |
} | |
- mdp3_irq_register(); | |
- | |
rc = mdp3_ctrl_dma_init(mfd, mdp3_session->dma); | |
if (rc) { | |
pr_err("dma init failed\n"); | |
@@ -622,7 +632,6 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd) | |
pr_err("display interface init failed\n"); | |
goto on_error; | |
} | |
- | |
mdp3_session->clk_on = 1; | |
mdp3_session->first_commit = true; | |
@@ -651,6 +660,9 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) | |
panel = mdp3_session->panel; | |
mutex_lock(&mdp3_session->lock); | |
+ if (panel && panel->set_backlight) | |
+ panel->set_backlight(panel, 0); | |
+ | |
if (!mdp3_session->status) { | |
pr_debug("fb%d is off already", mfd->index); | |
goto off_error; | |
@@ -660,21 +672,21 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) | |
mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P); | |
- rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf); | |
- if (rc) | |
- pr_debug("fail to stop the MDP3 dma\n"); | |
- | |
- | |
if (panel->event_handler) | |
rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL); | |
if (rc) | |
pr_err("fail to turn off the panel\n"); | |
+ rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf); | |
+ if (rc) | |
+ pr_debug("fail to stop the MDP3 dma\n"); | |
+ msleep(20); | |
+ | |
mdp3_irq_deregister(); | |
pr_debug("mdp3_ctrl_off stop clock\n"); | |
if (mdp3_session->clk_on) { | |
- rc = mdp3_clk_enable(0, 1); | |
+ rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P); | |
if (rc) | |
pr_err("mdp clock resource release failed\n"); | |
@@ -685,29 +697,23 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd) | |
if (rc) | |
pr_err("fail to turn off the panel\n"); | |
} | |
- mdp3_clk_unprepare(); | |
- | |
- pr_debug("mdp3_ctrl_off release bus\n"); | |
- rc = mdp3_ctrl_res_req_bus(mfd, 0); | |
- if (rc) | |
- pr_err("mdp bus resource release failed\n"); | |
- | |
- rc = mdp3_iommu_disable(MDP3_CLIENT_DMA_P); | |
- if (rc) | |
- pr_err("fail to dettach MDP DMA SMMU\n"); | |
mdp3_ctrl_notifier_unregister(mdp3_session, | |
&mdp3_session->mfd->mdp_sync_pt_data.notifier); | |
- mdp3_batfet_ctrl(false); | |
+ mdp3_enable_regulator(false); | |
mdp3_session->vsync_enabled = 0; | |
atomic_set(&mdp3_session->vsync_countdown, 0); | |
+ atomic_set(&mdp3_session->dma_done_cnt, 0); | |
mdp3_session->clk_on = 0; | |
+ mdp3_session->in_splash_screen = 0; | |
off_error: | |
mdp3_session->status = 0; | |
- mdp3_bufq_deinit(&mdp3_session->bufq_out); | |
- if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) { | |
- mdp3_session->overlay.id = MSMFB_NEW_REQUEST; | |
- mdp3_bufq_deinit(&mdp3_session->bufq_in); | |
+ if (!panel->panel_info.dynamic_switch_pending) { | |
+ mdp3_bufq_deinit(&mdp3_session->bufq_out); | |
+ if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) { | |
+ mdp3_session->overlay.id = MSMFB_NEW_REQUEST; | |
+ mdp3_bufq_deinit(&mdp3_session->bufq_in); | |
+ } | |
} | |
mutex_unlock(&mdp3_session->lock); | |
return 0; | |
@@ -754,6 +760,7 @@ static int mdp3_ctrl_reset_cmd(struct msm_fb_data_type *mfd) | |
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client); | |
mdp3_session->first_commit = true; | |
+ mdp3_session->in_splash_screen = 0; | |
reset_error: | |
mutex_unlock(&mdp3_session->lock); | |
@@ -789,16 +796,16 @@ static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd) | |
if (panel && panel->set_backlight) | |
panel->set_backlight(panel, 0); | |
+ rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL); | |
+ if (rc) | |
+ pr_err("fail to turn off panel\n"); | |
+ | |
rc = mdp3_dma->stop(mdp3_dma, mdp3_session->intf); | |
if (rc) { | |
- pr_err("fail to stop the MDP3 dma\n"); | |
+ pr_err("fail to stop the MDP3 dma %d\n", rc); | |
goto reset_error; | |
} | |
- rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL); | |
- if (rc) | |
- pr_err("fail to turn off panel\n"); | |
- | |
rc = mdp3_put_mdp_dsi_clk(); | |
if (rc) { | |
pr_err("fail to release mdp clocks\n"); | |
@@ -842,6 +849,7 @@ static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd) | |
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client); | |
mdp3_session->first_commit = true; | |
+ mdp3_session->in_splash_screen = 0; | |
reset_error: | |
mutex_unlock(&mdp3_session->lock); | |
@@ -889,16 +897,12 @@ static int mdp3_overlay_set(struct msm_fb_data_type *mfd, | |
mdp3_session->overlay = *req; | |
if (req->id == MSMFB_NEW_REQUEST) { | |
if (dma->source_config.stride != stride || | |
- dma->source_config.width != req->src.width || | |
- dma->source_config.height != req->src.height || | |
dma->source_config.format != format) { | |
- dma->source_config.width = req->src.width; | |
- dma->source_config.height = req->src.height, | |
dma->source_config.format = format; | |
dma->source_config.stride = stride; | |
- mdp3_clk_enable(1, 0); | |
- mdp3_session->dma->dma_config_source(dma); | |
- mdp3_clk_enable(0, 0); | |
+ dma->output_config.pack_pattern = | |
+ mdp3_ctrl_get_pack_pattern(req->src.format); | |
+ dma->update_src_cfg = true; | |
} | |
mdp3_session->overlay.id = 1; | |
req->id = 1; | |
@@ -915,7 +919,6 @@ static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx) | |
struct mdp3_session_data *mdp3_session = mfd->mdp.private1; | |
struct fb_info *fbi = mfd->fbi; | |
struct fb_fix_screeninfo *fix; | |
- struct mdss_panel_info *panel_info = mfd->panel_info; | |
int format; | |
fix = &fbi->fix; | |
@@ -923,14 +926,6 @@ static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx) | |
mutex_lock(&mdp3_session->lock); | |
if (mdp3_session->overlay.id == ndx && ndx == 1) { | |
- struct mdp3_dma *dma = mdp3_session->dma; | |
- dma->source_config.width = panel_info->xres, | |
- dma->source_config.height = panel_info->yres, | |
- dma->source_config.format = format; | |
- dma->source_config.stride = fix->line_length; | |
- mdp3_clk_enable(1, 0); | |
- mdp3_session->dma->dma_config_source(dma); | |
- mdp3_clk_enable(0, 0); | |
mdp3_session->overlay.id = MSMFB_NEW_REQUEST; | |
mdp3_bufq_deinit(&mdp3_session->bufq_in); | |
} else { | |
@@ -991,7 +986,7 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, | |
{ | |
struct mdp3_session_data *mdp3_session; | |
struct mdp3_img_data *data; | |
- struct mdss_panel_info *panel_info = mfd->panel_info; | |
+ struct mdss_panel_info *panel_info; | |
int rc = 0; | |
bool reset_done = false; | |
struct mdss_panel_data *panel; | |
@@ -999,6 +994,7 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, | |
if (!mfd || !mfd->mdp.private1) | |
return -EINVAL; | |
+ panel_info = mfd->panel_info; | |
mdp3_session = mfd->mdp.private1; | |
if (!mdp3_session || !mdp3_session->dma) | |
return -EINVAL; | |
@@ -1009,9 +1005,13 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, | |
} | |
panel = mdp3_session->panel; | |
- if (!mdp3_iommu_is_attached(MDP3_CLIENT_DMA_P)) { | |
+ if (mdp3_session->in_splash_screen) { | |
pr_debug("continuous splash screen, IOMMU not attached\n"); | |
- mdp3_ctrl_reset(mfd); | |
+ rc = mdp3_ctrl_reset(mfd); | |
+ if (rc) { | |
+ pr_err("fail to reset display\n"); | |
+ return -EINVAL; | |
+ } | |
reset_done = true; | |
} | |
@@ -1042,7 +1042,8 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, | |
MDP_NOTIFY_FRAME_DONE); | |
} | |
} | |
- | |
+ mdp3_session->dma_active = 1; | |
+ init_completion(&mdp3_session->dma_completion); | |
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED); | |
mdp3_bufq_push(&mdp3_session->bufq_out, data); | |
} | |
@@ -1050,7 +1051,8 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd, | |
if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) { | |
mdp3_release_splash_memory(mfd); | |
data = mdp3_bufq_pop(&mdp3_session->bufq_out); | |
- mdp3_put_img(data, MDP3_CLIENT_DMA_P); | |
+ if (data) | |
+ mdp3_put_img(data, MDP3_CLIENT_DMA_P); | |
} | |
if (mdp3_session->first_commit) { | |
@@ -1076,20 +1078,25 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) | |
struct mdp3_session_data *mdp3_session; | |
u32 offset; | |
int bpp; | |
- struct mdss_panel_info *panel_info = mfd->panel_info; | |
+ struct mdss_panel_info *panel_info; | |
int rc; | |
pr_debug("mdp3_ctrl_pan_display\n"); | |
if (!mfd || !mfd->mdp.private1) | |
return; | |
+ panel_info = mfd->panel_info; | |
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1; | |
if (!mdp3_session || !mdp3_session->dma) | |
return; | |
- if (!mdp3_iommu_is_attached(MDP3_CLIENT_DMA_P)) { | |
+ if (mdp3_session->in_splash_screen) { | |
pr_debug("continuous splash screen, IOMMU not attached\n"); | |
- mdp3_ctrl_reset(mfd); | |
+ rc = mdp3_ctrl_reset(mfd); | |
+ if (rc) { | |
+ pr_err("fail to reset display\n"); | |
+ return; | |
+ } | |
} | |
mutex_lock(&mdp3_session->lock); | |
@@ -1129,6 +1136,8 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd) | |
MDP_NOTIFY_FRAME_DONE); | |
} | |
} | |
+ mdp3_session->dma_active = 1; | |
+ init_completion(&mdp3_session->dma_completion); | |
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED); | |
} else { | |
pr_debug("mdp3_ctrl_pan_display no memory, stop interface"); | |
@@ -1275,7 +1284,7 @@ static int mdp3_histogram_start(struct mdp3_session_data *session, | |
return -EBUSY; | |
} | |
- mdp3_clk_enable(1, 0); | |
+ mdp3_res_update(1, 0, MDP3_CLIENT_DMA_P); | |
ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET); | |
if (ret) { | |
pr_err("mdp3_histogram_start reset error\n"); | |
@@ -1301,7 +1310,7 @@ static int mdp3_histogram_start(struct mdp3_session_data *session, | |
session->histo_status = 1; | |
histogram_start_err: | |
- mdp3_clk_enable(0, 0); | |
+ mdp3_res_update(0, 0, MDP3_CLIENT_DMA_P); | |
mutex_unlock(&session->histo_lock); | |
return ret; | |
} | |
@@ -1611,6 +1620,7 @@ static int mdp3_overlay_prepare(struct msm_fb_data_type *mfd, | |
{ | |
struct mdp_overlay_list ovlist; | |
struct mdp3_session_data *mdp3_session = mfd->mdp.private1; | |
+ struct mdp_overlay *req_list; | |
struct mdp_overlay *req; | |
int rc; | |
@@ -1627,12 +1637,15 @@ static int mdp3_overlay_prepare(struct msm_fb_data_type *mfd, | |
return -EINVAL; | |
} | |
- if (copy_from_user(req, ovlist.overlay_list[0], sizeof(*req))) | |
+ if (copy_from_user(&req_list, ovlist.overlay_list, sizeof(struct mdp_overlay*))) | |
+ return -EFAULT; | |
+ | |
+ if (copy_from_user(req, req_list, sizeof(*req))) | |
return -EFAULT; | |
rc = mdp3_overlay_set(mfd, req); | |
if (!IS_ERR_VALUE(rc)) { | |
- if (copy_to_user(ovlist.overlay_list[0], req, sizeof(*req))) | |
+ if (copy_to_user(req_list, req, sizeof(*req))) | |
return -EFAULT; | |
} | |
@@ -1751,6 +1764,65 @@ static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd, | |
return rc; | |
} | |
+int mdp3_wait_for_dma_done(struct mdp3_session_data *session) | |
+{ | |
+ int rc = 0; | |
+ | |
+ if (session->dma_active) { | |
+ rc = wait_for_completion_timeout(&session->dma_completion, | |
+ KOFF_TIMEOUT); | |
+ if (rc > 0) { | |
+ session->dma_active = 0; | |
+ rc = 0; | |
+ } else if (rc == 0) { | |
+ rc = -ETIME; | |
+ } | |
+ } | |
+ return rc; | |
+} | |
+ | |
+static int mdp3_update_panel_info(struct msm_fb_data_type *mfd, int mode) | |
+{ | |
+ int ret = 0; | |
+ struct mdp3_session_data *mdp3_session; | |
+ struct mdss_panel_data *panel; | |
+ u32 intf_type = 0; | |
+ | |
+ if (!mfd || !mfd->mdp.private1) | |
+ return -EINVAL; | |
+ | |
+ mdp3_session = mfd->mdp.private1; | |
+ panel = mdp3_session->panel; | |
+ | |
+ if (!panel->event_handler) | |
+ return 0; | |
+ ret = panel->event_handler(panel, MDSS_EVENT_DSI_DYNAMIC_SWITCH, | |
+ (void *)(unsigned long)mode); | |
+ if (ret) | |
+ pr_err("Dynamic switch to %s mode failed!\n", | |
+ mode ? "command" : "video"); | |
+ if (mode == 1) | |
+ mfd->panel.type = MIPI_CMD_PANEL; | |
+ else | |
+ mfd->panel.type = MIPI_VIDEO_PANEL; | |
+ | |
+ if (mfd->panel.type != MIPI_VIDEO_PANEL) | |
+ mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done; | |
+ | |
+ intf_type = mdp3_ctrl_get_intf_type(mfd); | |
+ mdp3_session->intf->cfg.type = intf_type; | |
+ mdp3_session->intf->available = 1; | |
+ mdp3_session->intf->in_use = 1; | |
+ mdp3_res->intf[intf_type].in_use = 1; | |
+ | |
+ mdp3_intf_init(mdp3_session->intf); | |
+ | |
+ mdp3_session->dma->output_config.out_sel = intf_type; | |
+ mdp3_session->status = mdp3_session->intf->active; | |
+ | |
+ return 0; | |
+} | |
+ | |
int mdp3_ctrl_init(struct msm_fb_data_type *mfd) | |
{ | |
struct device *dev = mfd->fbi->dev; | |
@@ -1773,6 +1845,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) | |
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler; | |
mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff; | |
mdp3_interface->lut_update = mdp3_ctrl_lut_update; | |
+ mdp3_interface->configure_panel = mdp3_update_panel_info; | |
mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL); | |
if (!mdp3_session) { | |
@@ -1825,6 +1898,9 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) | |
mdp3_session->vsync_timer.data = (u32)mdp3_session; | |
mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate; | |
mfd->mdp.private1 = mdp3_session; | |
+ init_completion(&mdp3_session->dma_completion); | |
+ if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) | |
+ mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done; | |
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group); | |
if (rc) { | |
@@ -1849,6 +1925,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) | |
if (mdp3_get_cont_spash_en()) { | |
mdp3_session->clk_on = 1; | |
+ mdp3_session->in_splash_screen = 1; | |
mdp3_ctrl_notifier_register(mdp3_session, | |
&mdp3_session->mfd->mdp_sync_pt_data.notifier); | |
} | |
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h | |
index cfad1d3..93356e2 100644 | |
--- a/drivers/video/msm/mdss/mdp3_ctrl.h | |
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2013-2014, 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 | |
@@ -50,6 +50,7 @@ struct mdp3_session_data { | |
struct mdp3_buffer_queue bufq_out; | |
struct work_struct clk_off_work; | |
struct work_struct dma_done_work; | |
+ atomic_t dma_done_cnt; | |
int histo_status; | |
struct mutex histo_lock; | |
int lut_sel; | |
@@ -61,6 +62,11 @@ struct mdp3_session_data { | |
int vsync_enabled; | |
atomic_t vsync_countdown; /* Used to count down */ | |
+ bool in_splash_screen; | |
+ | |
+ bool dma_active; | |
+ struct completion dma_completion; | |
+ int (*wait_for_dma_done)(struct mdp3_session_data *session); | |
}; | |
int mdp3_ctrl_init(struct msm_fb_data_type *mfd); | |
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c | |
index 7461e62..7afbf46 100644 | |
--- a/drivers/video/msm/mdss/mdp3_dma.c | |
+++ b/drivers/video/msm/mdss/mdp3_dma.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2013-2014, 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 | |
@@ -19,12 +19,17 @@ | |
#include "mdss_debug.h" | |
#define DMA_STOP_POLL_SLEEP_US 1000 | |
-#define DMA_STOP_POLL_TIMEOUT_US 32000 | |
+#define DMA_STOP_POLL_TIMEOUT_US 200000 | |
#define DMA_HISTO_RESET_TIMEOUT_MS 40 | |
#define DMA_LUT_CONFIG_MASK 0xfffffbe8 | |
#define DMA_CCS_CONFIG_MASK 0xfffffc17 | |
#define HIST_WAIT_TIMEOUT(frame) ((75 * HZ * (frame)) / 1000) | |
+#define VSYNC_SELECT 0x024 | |
+#define VSYNC_TOTAL_LINES_SHIFT 21 | |
+#define VSYNC_COUNT_MASK 0x7ffff | |
+#define VSYNC_THRESH_CONT_SHIFT 16 | |
+ | |
static void mdp3_vsync_intr_handler(int type, void *arg) | |
{ | |
struct mdp3_dma *dma = (struct mdp3_dma *)arg; | |
@@ -267,32 +272,48 @@ static void mdp3_dma_clk_auto_gating(struct mdp3_dma *dma, int enable) | |
} | |
} | |
-static int mdp3_dma_sync_config(struct mdp3_dma *dma, | |
- struct mdp3_dma_source *source_config) | |
+ | |
+int mdp3_dma_sync_config(struct mdp3_dma *dma, | |
+ struct mdp3_dma_source *source_config, struct mdp3_tear_check *te) | |
{ | |
- u32 sync_config; | |
+ u32 vsync_clk_speed_hz, vclks_line, cfg; | |
+ int porch = source_config->vporch; | |
+ int height = source_config->height; | |
+ int total_lines = height + porch; | |
int dma_sel = dma->dma_sel; | |
- pr_debug("mdp3_dma_sync_config\n"); | |
+ vsync_clk_speed_hz = MDP_VSYNC_CLK_RATE; | |
- if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { | |
- int porch = source_config->vporch; | |
- int height = source_config->height; | |
- int vtotal = height + porch; | |
- sync_config = vtotal << 21; | |
- sync_config |= source_config->vsync_count; | |
- sync_config |= BIT(19); | |
- sync_config |= BIT(20); | |
- | |
- MDP3_REG_WRITE(MDP3_REG_SYNC_CONFIG_0 + dma_sel, sync_config); | |
- MDP3_REG_WRITE(MDP3_REG_VSYNC_SEL, 0x024); | |
- MDP3_REG_WRITE(MDP3_REG_PRIMARY_VSYNC_INIT_VAL + dma_sel, | |
- height); | |
- MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, 0x5); | |
- MDP3_REG_WRITE(MDP3_REG_SYNC_THRESH_0 + dma_sel, (4 << 16 | 2)); | |
- MDP3_REG_WRITE(MDP3_REG_PRIMARY_START_P0S + dma_sel, porch); | |
- MDP3_REG_WRITE(MDP3_REG_TEAR_CHECK_EN, 0x1); | |
+ cfg = total_lines << VSYNC_TOTAL_LINES_SHIFT; | |
+ total_lines *= te->frame_rate; | |
+ | |
+ vclks_line = (total_lines) ? vsync_clk_speed_hz / total_lines : 0; | |
+ | |
+ cfg |= BIT(19); | |
+ if (te->hw_vsync_mode) | |
+ cfg |= BIT(20); | |
+ | |
+ if (te->refx100) { | |
+ vclks_line = vclks_line * te->frame_rate * | |
+ 100 / te->refx100; | |
+ } else { | |
+ pr_warn("refx100 cannot be zero! Use 6000 as default\n"); | |
+ vclks_line = vclks_line * te->frame_rate * | |
+ 100 / 6000; | |
} | |
+ | |
+ cfg |= (vclks_line & VSYNC_COUNT_MASK); | |
+ | |
+ MDP3_REG_WRITE(MDP3_REG_SYNC_CONFIG_0 + dma_sel, cfg); | |
+ MDP3_REG_WRITE(MDP3_REG_VSYNC_SEL, VSYNC_SELECT); | |
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_VSYNC_INIT_VAL + dma_sel, | |
+ te->vsync_init_val); | |
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, te->rd_ptr_irq); | |
+ MDP3_REG_WRITE(MDP3_REG_SYNC_THRESH_0 + dma_sel, | |
+ ((te->sync_threshold_continue << VSYNC_THRESH_CONT_SHIFT) | | |
+ te->sync_threshold_start)); | |
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_START_P0S + dma_sel, te->start_pos); | |
+ MDP3_REG_WRITE(MDP3_REG_TEAR_CHECK_EN, te->tear_check_en); | |
return 0; | |
} | |
@@ -325,8 +346,6 @@ static int mdp3_dmap_config(struct mdp3_dma *dma, | |
dma->source_config = *source_config; | |
dma->output_config = *output_config; | |
- mdp3_dma_sync_config(dma, source_config); | |
- | |
mdp3_irq_enable(MDP3_INTR_LCDC_UNDERFLOW); | |
mdp3_dma_callback_setup(dma); | |
return 0; | |
@@ -340,6 +359,8 @@ static void mdp3_dmap_config_source(struct mdp3_dma *dma) | |
dma_p_cfg_reg = MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG); | |
dma_p_cfg_reg &= ~MDP3_DMA_IBUF_FORMAT_MASK; | |
dma_p_cfg_reg |= source_config->format << 25; | |
+ dma_p_cfg_reg &= ~MDP3_DMA_PACK_PATTERN_MASK; | |
+ dma_p_cfg_reg |= dma->output_config.pack_pattern << 8; | |
dma_p_size = source_config->width | (source_config->height << 16); | |
@@ -377,7 +398,6 @@ static int mdp3_dmas_config(struct mdp3_dma *dma, | |
dma->source_config = *source_config; | |
dma->output_config = *output_config; | |
- mdp3_dma_sync_config(dma, source_config); | |
mdp3_dma_callback_setup(dma); | |
return 0; | |
@@ -607,6 +627,13 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf, | |
ATRACE_END("mdp3_wait_for_dma_comp"); | |
} | |
} | |
+ if (dma->update_src_cfg) { | |
+ if (dma->output_config.out_sel == | |
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO && intf->active) | |
+ pr_err("configuring dma source while dma is active\n"); | |
+ dma->dma_config_source(dma); | |
+ dma->update_src_cfg = false; | |
+ } | |
spin_lock_irqsave(&dma->dma_lock, flag); | |
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf); | |
dma->source_config.buf = buf; | |
@@ -923,6 +950,7 @@ int mdp3_dma_init(struct mdp3_dma *dma) | |
switch (dma->dma_sel) { | |
case MDP3_DMA_P: | |
dma->dma_config = mdp3_dmap_config; | |
+ dma->dma_sync_config = mdp3_dma_sync_config; | |
dma->dma_config_source = mdp3_dmap_config_source; | |
dma->config_cursor = mdp3_dmap_cursor_config; | |
dma->config_ccs = mdp3_dmap_ccs_config; | |
@@ -939,6 +967,7 @@ int mdp3_dma_init(struct mdp3_dma *dma) | |
break; | |
case MDP3_DMA_S: | |
dma->dma_config = mdp3_dmas_config; | |
+ dma->dma_sync_config = mdp3_dma_sync_config; | |
dma->dma_config_source = mdp3_dmas_config_source; | |
dma->config_cursor = NULL; | |
dma->config_ccs = NULL; | |
@@ -966,6 +995,7 @@ int mdp3_dma_init(struct mdp3_dma *dma) | |
dma->vsync_client.handler = NULL; | |
dma->vsync_client.arg = NULL; | |
dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE; | |
+ dma->update_src_cfg = false; | |
memset(&dma->cursor, 0, sizeof(dma->cursor)); | |
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config)); | |
@@ -1057,7 +1087,9 @@ int dsi_video_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg) | |
temp |= BIT(2); | |
MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_CTL_POLARITY, temp); | |
- MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL, 0x800000ff); | |
+ v->underflow_color |= 0x80000000; | |
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL, v->underflow_color); | |
+ | |
return 0; | |
} | |
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h | |
index 207168f..d07e06d 100644 | |
--- a/drivers/video/msm/mdss/mdp3_dma.h | |
+++ b/drivers/video/msm/mdss/mdp3_dma.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2013-2014, 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 | |
@@ -233,6 +233,19 @@ struct mdp3_notification { | |
void *arg; | |
}; | |
+struct mdp3_tear_check { | |
+ int frame_rate; | |
+ bool hw_vsync_mode; | |
+ u32 tear_check_en; | |
+ u32 sync_cfg_height; | |
+ u32 vsync_init_val; | |
+ u32 sync_threshold_start; | |
+ u32 sync_threshold_continue; | |
+ u32 start_pos; | |
+ u32 rd_ptr_irq; | |
+ u32 refx100; | |
+}; | |
+ | |
struct mdp3_intf; | |
struct mdp3_dma { | |
@@ -259,11 +272,15 @@ struct mdp3_dma { | |
int histo_state; | |
struct mdp3_dma_histogram_data histo_data; | |
unsigned int vsync_status; | |
+ bool update_src_cfg; | |
int (*dma_config)(struct mdp3_dma *dma, | |
struct mdp3_dma_source *source_config, | |
struct mdp3_dma_output_config *output_config); | |
+ int (*dma_sync_config)(struct mdp3_dma *dma, struct mdp3_dma_source | |
+ *source_config, struct mdp3_tear_check *te); | |
+ | |
void (*dma_config_source)(struct mdp3_dma *dma); | |
int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf); | |
@@ -318,6 +335,7 @@ struct mdp3_video_intf_cfg { | |
int hsync_polarity; | |
int vsync_polarity; | |
int de_polarity; | |
+ int underflow_color; | |
}; | |
struct mdp3_dsi_cmd_intf_cfg { | |
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h | |
index 39690ef..c40ee47 100644 | |
--- a/drivers/video/msm/mdss/mdp3_hwio.h | |
+++ b/drivers/video/msm/mdss/mdp3_hwio.h | |
@@ -120,6 +120,7 @@ | |
/*DMA MASK*/ | |
#define MDP3_DMA_IBUF_FORMAT_MASK 0x06000000 | |
+#define MDP3_DMA_PACK_PATTERN_MASK 0x00003f00 | |
/*MISR*/ | |
#define MDP3_REG_MODE_CLK 0x000D0000 | |
diff --git a/drivers/video/msm/mdss/mdp3_ppp.c b/drivers/video/msm/mdss/mdp3_ppp.c | |
index 1107a6d..f21f3ef 100644 | |
--- a/drivers/video/msm/mdss/mdp3_ppp.c | |
+++ b/drivers/video/msm/mdss/mdp3_ppp.c | |
@@ -100,6 +100,7 @@ struct ppp_status { | |
struct timer_list free_bw_timer; | |
struct work_struct free_bw_work; | |
bool bw_on; | |
+ bool bw_optimal; | |
}; | |
static struct ppp_status *ppp_stat; | |
@@ -272,11 +273,11 @@ int mdp3_ppp_pipe_wait(void) | |
int ret = 1; | |
/* | |
- * wait 40 ms for ppp operation to complete before declaring | |
+ * wait 200 ms for ppp operation to complete before declaring | |
* the MDP hung | |
*/ | |
ret = wait_for_completion_timeout( | |
- &ppp_stat->ppp_comp, msecs_to_jiffies(40)); | |
+ &ppp_stat->ppp_comp, msecs_to_jiffies(200)); | |
if (!ret) | |
pr_err("%s: Timed out waiting for the MDP.\n", | |
__func__); | |
@@ -338,31 +339,64 @@ void mdp3_ppp_kickoff(void) | |
mdp3_irq_disable(MDP3_PPP_DONE); | |
} | |
+int mdp3_ppp_vote_update(struct msm_fb_data_type *mfd) | |
+{ | |
+ struct mdss_panel_info *panel_info = mfd->panel_info; | |
+ uint64_t req_bw = 0, ab = 0, ib = 0; | |
+ int rate = 0; | |
+ int rc = 0; | |
+ if (!ppp_stat->bw_on) | |
+ pr_err("%s: PPP vote update in wrong state\n", __func__); | |
+ | |
+ rate = MDP_BLIT_CLK_RATE; | |
+ req_bw = panel_info->xres * panel_info->yres * | |
+ panel_info->mipi.frame_rate * | |
+ MDP_PPP_MAX_BPP * | |
+ MDP_PPP_DYNAMIC_FACTOR * | |
+ MDP_PPP_MAX_READ_WRITE; | |
+ ib = (req_bw * 3) / 2; | |
+ | |
+ if (ppp_stat->bw_optimal) | |
+ ab = ib / 2; | |
+ else | |
+ ab = req_bw; | |
+ rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib); | |
+ if (rc < 0) { | |
+ pr_err("%s: scale_set_quota failed\n", __func__); | |
+ return rc; | |
+ } | |
+ return rc; | |
+} | |
+ | |
int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off) | |
{ | |
struct mdss_panel_info *panel_info = mfd->panel_info; | |
- uint64_t ab = 0, ib = 0; | |
+ uint64_t req_bw = 0, ab = 0, ib = 0; | |
int rate = 0; | |
int rc; | |
if (on_off) { | |
rate = MDP_BLIT_CLK_RATE; | |
- ab = panel_info->xres * panel_info->yres * | |
+ req_bw = panel_info->xres * panel_info->yres * | |
panel_info->mipi.frame_rate * | |
MDP_PPP_MAX_BPP * | |
MDP_PPP_DYNAMIC_FACTOR * | |
MDP_PPP_MAX_READ_WRITE; | |
- ib = (ab * 3) / 2; | |
+ ib = (req_bw * 3) / 2; | |
+ if (ppp_stat->bw_optimal) | |
+ ab = ib / 2; | |
+ else | |
+ ab = req_bw; | |
} | |
mdp3_clk_set_rate(MDP3_CLK_CORE, rate, MDP3_CLIENT_PPP); | |
- rc = mdp3_clk_enable(on_off, 0); | |
+ rc = mdp3_res_update(on_off, 0, MDP3_CLIENT_PPP); | |
if (rc < 0) { | |
pr_err("%s: mdp3_clk_enable failed\n", __func__); | |
return rc; | |
} | |
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib); | |
if (rc < 0) { | |
- mdp3_clk_enable(!on_off, 0); | |
+ mdp3_res_update(!on_off, 0, MDP3_CLIENT_PPP); | |
pr_err("%s: scale_set_quota failed\n", __func__); | |
return rc; | |
} | |
@@ -370,6 +404,22 @@ int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off) | |
return 0; | |
} | |
+bool mdp3_optimal_bw(struct blit_req_list *req) | |
+{ | |
+ int i, solid_fill = 0; | |
+ | |
+ if (!req || (ppp_stat->req_q.count > 1)) | |
+ return false; | |
+ | |
+ for (i = 0; i < req->count; i++) { | |
+ if (req->req_list[i].flags & MDP_SOLID_FILL) | |
+ solid_fill++; | |
+ } | |
+ if ((req->count - solid_fill) <= 1) | |
+ return true; | |
+ return false; | |
+} | |
+ | |
void mdp3_start_ppp(struct ppp_blit_op *blit_op) | |
{ | |
/* Wait for the pipe to clear */ | |
@@ -825,24 +875,6 @@ int mdp3_ppp_start_blit(struct msm_fb_data_type *mfd, | |
if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0)) | |
return 0; | |
- if (req->flags & MDP_ROT_90) { | |
- if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) || | |
- (req->dst_rect.w != req->src_rect.h))) || | |
- ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || | |
- (req->dst_rect.h != req->src_rect.w)))) { | |
- pr_err("mdp_ppp: error scaling when size is 1!\n"); | |
- return -EINVAL; | |
- } | |
- } else { | |
- if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) || | |
- (req->dst_rect.h != req->src_rect.h))) || | |
- ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || | |
- (req->dst_rect.w != req->src_rect.w)))) { | |
- pr_err("mdp_ppp: error scaling when size is 1!\n"); | |
- return -EINVAL; | |
- } | |
- } | |
- | |
/* MDP width split workaround */ | |
remainder = (req->dst_rect.w) % 16; | |
ret = ppp_get_bpp(req->dst.format, mfd->fb_imgType); | |
@@ -1002,14 +1034,10 @@ void mdp3_free_fw_timer_func(unsigned long arg) | |
static void mdp3_free_bw_wq_handler(struct work_struct *work) | |
{ | |
struct msm_fb_data_type *mfd = ppp_stat->mfd; | |
- int rc; | |
mutex_lock(&ppp_stat->config_ppp_mutex); | |
if (ppp_stat->bw_on) { | |
mdp3_ppp_turnon(mfd, 0); | |
- rc = mdp3_iommu_disable(MDP3_CLIENT_PPP); | |
- if (rc < 0) | |
- WARN(1, "Unable to disable ppp iommu\n"); | |
} | |
mutex_unlock(&ppp_stat->config_ppp_mutex); | |
} | |
@@ -1028,15 +1056,9 @@ static void mdp3_ppp_blit_wq_handler(struct work_struct *work) | |
} | |
if (!ppp_stat->bw_on) { | |
- rc = mdp3_iommu_enable(MDP3_CLIENT_PPP); | |
- if (rc < 0) { | |
- mutex_unlock(&ppp_stat->config_ppp_mutex); | |
- pr_err("%s: mdp3_iommu_enable failed\n", __func__); | |
- return; | |
- } | |
+ ppp_stat->bw_optimal = mdp3_optimal_bw(req); | |
mdp3_ppp_turnon(mfd, 1); | |
if (rc < 0) { | |
- mdp3_iommu_disable(MDP3_CLIENT_PPP); | |
mutex_unlock(&ppp_stat->config_ppp_mutex); | |
pr_err("%s: Enable ppp resources failed\n", __func__); | |
return; | |
@@ -1069,6 +1091,10 @@ static void mdp3_ppp_blit_wq_handler(struct work_struct *work) | |
if (ppp_stat->wait_for_pop) | |
complete(&ppp_stat->pop_q_comp); | |
mutex_unlock(&ppp_stat->req_mutex); | |
+ if (req && (ppp_stat->bw_optimal != mdp3_optimal_bw(req))) { | |
+ ppp_stat->bw_optimal = !ppp_stat->bw_optimal; | |
+ mdp3_ppp_vote_update(mfd); | |
+ } | |
} | |
mod_timer(&ppp_stat->free_bw_timer, jiffies + | |
msecs_to_jiffies(MDP_RELEASE_BW_TIMEOUT)); | |
diff --git a/drivers/video/msm/mdss/mdp3_ppp.h b/drivers/video/msm/mdss/mdp3_ppp.h | |
index 9753e94..a0ad3c3 100644 | |
--- a/drivers/video/msm/mdss/mdp3_ppp.h | |
+++ b/drivers/video/msm/mdss/mdp3_ppp.h | |
@@ -391,7 +391,7 @@ struct ppp_edge_rep { | |
uint32_t ppp_bpp(uint32_t type); | |
uint32_t ppp_src_config(uint32_t type); | |
uint32_t ppp_out_config(uint32_t type); | |
-uint32_t ppp_pack_pattern(uint32_t type); | |
+uint32_t ppp_pack_pattern(uint32_t type, uint32_t yuv2rgb); | |
uint32_t ppp_dst_op_reg(uint32_t type); | |
uint32_t ppp_src_op_reg(uint32_t type); | |
bool ppp_per_p_alpha(uint32_t type); | |
diff --git a/drivers/video/msm/mdss/mdp3_ppp_data.c b/drivers/video/msm/mdss/mdp3_ppp_data.c | |
index e562ad3..5748842 100644 | |
--- a/drivers/video/msm/mdss/mdp3_ppp_data.c | |
+++ b/drivers/video/msm/mdss/mdp3_ppp_data.c | |
@@ -88,6 +88,35 @@ const uint32_t pack_patt_lut[MDP_IMGTYPE_LIMIT] = { | |
CLR_G, CLR_R, 8), | |
}; | |
+const uint32_t swapped_pack_patt_lut[MDP_IMGTYPE_LIMIT] = { | |
+ [MDP_RGB_565] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), | |
+ [MDP_BGR_565] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), | |
+ [MDP_RGB_888] = PPP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 8), | |
+ [MDP_BGR_888] = PPP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8), | |
+ [MDP_BGRA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, | |
+ CLR_G, CLR_B, 8), | |
+ [MDP_RGBA_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, | |
+ CLR_G, CLR_R, 8), | |
+ [MDP_ARGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, | |
+ CLR_G, CLR_R, 8), | |
+ [MDP_XRGB_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, | |
+ CLR_G, CLR_R, 8), | |
+ [MDP_RGBX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, | |
+ CLR_G, CLR_R, 8), | |
+ [MDP_Y_CRCB_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8), | |
+ [MDP_Y_CBCR_H2V2] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8), | |
+ [MDP_Y_CBCR_H2V2_ADRENO] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, | |
+ CLR_CB, 8), | |
+ [MDP_Y_CBCR_H2V2_VENUS] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, | |
+ CLR_CB, 8), | |
+ [MDP_YCRYCB_H2V1] = PPP_GET_PACK_PATTERN(CLR_Y, | |
+ CLR_CB, CLR_Y, CLR_CR, 8), | |
+ [MDP_Y_CBCR_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8), | |
+ [MDP_Y_CRCB_H2V1] = PPP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8), | |
+ [MDP_BGRX_8888] = PPP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, | |
+ CLR_G, CLR_B, 8), | |
+}; | |
+ | |
const uint32_t dst_op_reg[MDP_IMGTYPE_LIMIT] = { | |
[MDP_Y_CRCB_H2V2] = PPP_OP_DST_CHROMA_420, | |
[MDP_Y_CBCR_H2V2] = PPP_OP_DST_CHROMA_420, | |
@@ -1530,10 +1559,13 @@ uint32_t ppp_out_config(uint32_t type) | |
return out_cfg_lut[type]; | |
} | |
-uint32_t ppp_pack_pattern(uint32_t type) | |
+uint32_t ppp_pack_pattern(uint32_t type, uint32_t yuv2rgb) | |
{ | |
if (MDP_IS_IMGTYPE_BAD(type)) | |
return 0; | |
+ if (yuv2rgb) | |
+ return swapped_pack_patt_lut[type]; | |
+ | |
return pack_patt_lut[type]; | |
} | |
diff --git a/drivers/video/msm/mdss/mdp3_ppp_hwio.c b/drivers/video/msm/mdss/mdp3_ppp_hwio.c | |
index eb01d00..8c5d771 100644 | |
--- a/drivers/video/msm/mdss/mdp3_ppp_hwio.c | |
+++ b/drivers/video/msm/mdss/mdp3_ppp_hwio.c | |
@@ -486,7 +486,7 @@ int load_csc_matrix(int matrix_type, struct ppp_csc_table *csc) | |
return load_secondary_matrix(csc); | |
} | |
-int config_ppp_src(struct ppp_img_desc *src) | |
+int config_ppp_src(struct ppp_img_desc *src, uint32_t yuv2rgb) | |
{ | |
uint32_t val; | |
@@ -510,12 +510,12 @@ int config_ppp_src(struct ppp_img_desc *src) | |
val |= (src->roi.x % 2) ? PPP_SRC_BPP_ROI_ODD_X : 0; | |
val |= (src->roi.y % 2) ? PPP_SRC_BPP_ROI_ODD_Y : 0; | |
PPP_WRITEL(val, MDP3_PPP_SRC_FORMAT); | |
- PPP_WRITEL(ppp_pack_pattern(src->color_fmt), | |
+ PPP_WRITEL(ppp_pack_pattern(src->color_fmt, yuv2rgb), | |
MDP3_PPP_SRC_UNPACK_PATTERN1); | |
return 0; | |
} | |
-int config_ppp_out(struct ppp_img_desc *dst) | |
+int config_ppp_out(struct ppp_img_desc *dst, uint32_t yuv2rgb) | |
{ | |
uint32_t val; | |
bool pseudoplanr_output = false; | |
@@ -534,7 +534,7 @@ int config_ppp_out(struct ppp_img_desc *dst) | |
if (pseudoplanr_output) | |
val |= PPP_DST_PLANE_PSEUDOPLN; | |
PPP_WRITEL(val, MDP3_PPP_OUT_FORMAT); | |
- PPP_WRITEL(ppp_pack_pattern(dst->color_fmt), | |
+ PPP_WRITEL(ppp_pack_pattern(dst->color_fmt, yuv2rgb), | |
MDP3_PPP_OUT_PACK_PATTERN1); | |
val = ((dst->roi.height & MDP3_PPP_XY_MASK) << MDP3_PPP_XY_OFFSET) | | |
@@ -573,7 +573,7 @@ int config_ppp_background(struct ppp_img_desc *bg) | |
PPP_WRITEL(ppp_src_config(bg->color_fmt), | |
MDP3_PPP_BG_FORMAT); | |
- PPP_WRITEL(ppp_pack_pattern(bg->color_fmt), | |
+ PPP_WRITEL(ppp_pack_pattern(bg->color_fmt, 0), | |
MDP3_PPP_BG_UNPACK_PATTERN1); | |
return 0; | |
} | |
@@ -953,7 +953,7 @@ int config_ppp_scale(struct ppp_blit_op *blit_op, uint32_t *pppop_reg_ptr) | |
PPP_WRITEL(phase_step_y, MDP3_PPP_SCALE_PHASEY_STEP); | |
- if (dstW > src->roi.width || dstW > src->roi.height) | |
+ if (dstW > src->roi.width || dstH > src->roi.height) | |
ppp_load_up_lut(); | |
if (mdp_blur) | |
@@ -1108,6 +1108,7 @@ int config_ppp_rotation(uint32_t mdp_op, uint32_t *pppop_reg_ptr) | |
int config_ppp_op_mode(struct ppp_blit_op *blit_op) | |
{ | |
+ uint32_t yuv2rgb; | |
uint32_t ppp_operation_reg = 0; | |
int sv_slice, sh_slice; | |
int dv_slice, dh_slice; | |
@@ -1153,6 +1154,7 @@ int config_ppp_op_mode(struct ppp_blit_op *blit_op) | |
config_ppp_csc(blit_op->src.color_fmt, | |
blit_op->dst.color_fmt, &ppp_operation_reg); | |
+ yuv2rgb = ppp_operation_reg & PPP_OP_CONVERT_YCBCR2RGB; | |
if (blit_op->mdp_op & MDPOP_DITHER) | |
ppp_operation_reg |= PPP_OP_DITHER_EN; | |
@@ -1197,8 +1199,8 @@ int config_ppp_op_mode(struct ppp_blit_op *blit_op) | |
config_ppp_blend(blit_op, &ppp_operation_reg); | |
- config_ppp_src(&blit_op->src); | |
- config_ppp_out(&blit_op->dst); | |
+ config_ppp_src(&blit_op->src, yuv2rgb); | |
+ config_ppp_out(&blit_op->dst, yuv2rgb); | |
PPP_WRITEL(ppp_operation_reg, MDP3_PPP_OP_MODE); | |
mb(); | |
return 0; | |
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h | |
index a11bd5f..e8c938b 100644 | |
--- a/drivers/video/msm/mdss/mdss.h | |
+++ b/drivers/video/msm/mdss/mdss.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -79,6 +79,30 @@ struct mdss_intr { | |
spinlock_t lock; | |
}; | |
+struct mdss_fudge_factor { | |
+ u32 numer; | |
+ u32 denom; | |
+}; | |
+ | |
+struct mdss_prefill_data { | |
+ u32 ot_bytes; | |
+ u32 y_buf_bytes; | |
+ u32 y_scaler_lines_bilinear; | |
+ u32 y_scaler_lines_caf; | |
+ u32 post_scaler_pixels; | |
+ u32 pp_pixels; | |
+ u32 fbc_lines; | |
+}; | |
+ | |
+enum mdss_hw_index { | |
+ MDSS_HW_MDP, | |
+ MDSS_HW_DSI0, | |
+ MDSS_HW_DSI1, | |
+ MDSS_HW_HDMI, | |
+ MDSS_HW_EDP, | |
+ MDSS_MAX_HW_BLK | |
+}; | |
+ | |
struct mdss_data_type { | |
u32 mdp_rev; | |
struct clk *mdp_clk[MDSS_MAX_CLK]; | |
@@ -105,6 +129,7 @@ struct mdss_data_type { | |
u32 has_no_lut_read; | |
u8 has_wb_ad; | |
+ u32 rotator_ot_limit; | |
u32 mdp_irq_mask; | |
u32 mdp_hist_irq_mask; | |
@@ -115,8 +140,8 @@ struct mdss_data_type { | |
unsigned long min_mdp_clk; | |
u32 res_init; | |
- u32 bus_hdl; | |
+ u32 highest_bank_bit; | |
u32 smp_mb_cnt; | |
u32 smp_mb_size; | |
u32 smp_mb_per_pipe; | |
@@ -126,6 +151,19 @@ struct mdss_data_type { | |
u32 max_bw_low; | |
u32 max_bw_high; | |
+ u32 axi_port_cnt; | |
+ u32 curr_bw_uc_idx; | |
+ u32 bus_hdl; | |
+ struct msm_bus_scale_pdata *bus_scale_table; | |
+ | |
+ struct mdss_fudge_factor ab_factor; | |
+ struct mdss_fudge_factor ib_factor; | |
+ struct mdss_fudge_factor ib_factor_overlap; | |
+ struct mdss_fudge_factor clk_factor; | |
+ | |
+ u32 *clock_levels; | |
+ u32 nclk_lvl; | |
+ | |
struct mdss_hw_settings *hw_settings; | |
struct mdss_mdp_pipe *vig_pipes; | |
@@ -163,22 +201,18 @@ struct mdss_data_type { | |
struct early_suspend early_suspend; | |
struct mdss_debug_inf debug_inf; | |
- int current_bus_idx; | |
bool mixer_switched; | |
struct mdss_panel_cfg pan_cfg; | |
int handoff_pending; | |
-}; | |
-extern struct mdss_data_type *mdss_res; | |
+ struct mdss_prefill_data prefill_data; | |
+ bool ulps; | |
+ int iommu_ref_cnt; | |
-enum mdss_hw_index { | |
- MDSS_HW_MDP, | |
- MDSS_HW_DSI0, | |
- MDSS_HW_DSI1, | |
- MDSS_HW_HDMI, | |
- MDSS_HW_EDP, | |
- MDSS_MAX_HW_BLK | |
+ u64 ab[MDSS_MAX_HW_BLK]; | |
+ u64 ib[MDSS_MAX_HW_BLK]; | |
}; | |
+extern struct mdss_data_type *mdss_res; | |
struct mdss_hw { | |
u32 hw_ndx; | |
@@ -191,6 +225,8 @@ void mdss_enable_irq(struct mdss_hw *hw); | |
void mdss_disable_irq(struct mdss_hw *hw); | |
void mdss_disable_irq_nosync(struct mdss_hw *hw); | |
void mdss_bus_bandwidth_ctrl(int enable); | |
+int mdss_iommu_ctrl(int enable); | |
+int mdss_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota); | |
static inline struct ion_client *mdss_get_ionclient(void) | |
{ | |
diff --git a/drivers/video/msm/mdss/mdss_debug.c b/drivers/video/msm/mdss/mdss_debug.c | |
index 0d0240f..aeb3f86 100644 | |
--- a/drivers/video/msm/mdss/mdss_debug.c | |
+++ b/drivers/video/msm/mdss/mdss_debug.c | |
@@ -30,21 +30,6 @@ | |
#define GROUP_BYTES 4 | |
#define ROW_BYTES 16 | |
#define MAX_VSYNC_COUNT 0xFFFFFFF | |
-struct mdss_debug_data { | |
- struct dentry *root; | |
- struct list_head base_list; | |
-}; | |
- | |
-struct mdss_debug_base { | |
- struct mdss_debug_data *mdd; | |
- void __iomem *base; | |
- size_t off; | |
- size_t cnt; | |
- size_t max_offset; | |
- char *buf; | |
- size_t buf_len; | |
- struct list_head head; | |
-}; | |
static int mdss_debug_base_open(struct inode *inode, struct file *file) | |
{ | |
@@ -265,12 +250,14 @@ int mdss_debug_register_base(const char *name, void __iomem *base, | |
if (!dbg) | |
return -ENOMEM; | |
+ if (name) | |
+ strlcpy(dbg->name, name, sizeof(dbg->name)); | |
dbg->base = base; | |
dbg->max_offset = max_offset; | |
dbg->off = 0; | |
dbg->cnt = DEFAULT_BASE_REG_CNT; | |
- if (name) | |
+ if (name && strcmp(name, "mdp")) | |
prefix_len = snprintf(dn, sizeof(dn), "%s_", name); | |
strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len); | |
@@ -395,6 +382,11 @@ int mdss_debugfs_init(struct mdss_data_type *mdata) | |
debugfs_create_u32("min_mdp_clk", 0644, mdd->root, | |
(u32 *)&mdata->min_mdp_clk); | |
+ if (mdss_create_xlog_debug(mdd)) { | |
+ mdss_debugfs_cleanup(mdd); | |
+ return -ENODEV; | |
+ } | |
+ | |
mdata->debug_inf.debug_data = mdd; | |
return 0; | |
@@ -410,6 +402,29 @@ int mdss_debugfs_remove(struct mdss_data_type *mdata) | |
return 0; | |
} | |
+void mdss_dump_reg(char __iomem *base, int len) | |
+{ | |
+ char *addr; | |
+ u32 x0, x4, x8, xc; | |
+ int i; | |
+ | |
+ addr = base; | |
+ if (len % 16) | |
+ len += 16; | |
+ len /= 16; | |
+ | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ for (i = 0; i < len; i++) { | |
+ x0 = readl_relaxed(addr+0x0); | |
+ x4 = readl_relaxed(addr+0x4); | |
+ x8 = readl_relaxed(addr+0x8); | |
+ xc = readl_relaxed(addr+0xc); | |
+ pr_info("%p : %08x %08x %08x %08x\n", addr, x0, x4, x8, xc); | |
+ addr += 16; | |
+ } | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
+} | |
+ | |
int vsync_count; | |
static struct mdss_mdp_misr_map { | |
u32 ctrl_reg; | |
diff --git a/drivers/video/msm/mdss/mdss_debug.h b/drivers/video/msm/mdss/mdss_debug.h | |
index 984caab..684bd45 100644 | |
--- a/drivers/video/msm/mdss/mdss_debug.h | |
+++ b/drivers/video/msm/mdss/mdss_debug.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -14,13 +14,56 @@ | |
#ifndef MDSS_DEBUG_H | |
#define MDSS_DEBUG_H | |
+#include <stdarg.h> | |
#include "mdss.h" | |
+#include "mdss_mdp_trace.h" | |
#define MISR_POLL_SLEEP 2000 | |
#define MISR_POLL_TIMEOUT 32000 | |
#define MISR_CRC_BATCH_CFG 0x101 | |
+#define DATA_LIMITER (-1) | |
+#define XLOG_TOUT_DATA_LIMITER (NULL) | |
+#define XLOG_FUNC_ENTRY 0x1111 | |
+#define XLOG_FUNC_EXIT 0x2222 | |
+#define MDSS_REG_BLOCK_NAME_LEN (5) | |
+ | |
+#define MDSS_XLOG(...) mdss_xlog(__func__, ##__VA_ARGS__, DATA_LIMITER) | |
+#define MDSS_XLOG_TOUT_HANDLER(...) \ | |
+ mdss_xlog_tout_handler(__func__, ##__VA_ARGS__, XLOG_TOUT_DATA_LIMITER) | |
+ | |
+#define ATRACE_END(name) trace_tracing_mark_write(current->tgid, name, 0) | |
+#define ATRACE_BEGIN(name) trace_tracing_mark_write(current->tgid, name, 1) | |
+#define ATRACE_FUNC() ATRACE_BEGIN(__func__) | |
+ | |
+#define ATRACE_INT(name, value) \ | |
+ trace_mdp_trace_counter(current->tgid, name, value) | |
#ifdef CONFIG_DEBUG_FS | |
+struct mdss_debug_base { | |
+ struct mdss_debug_data *mdd; | |
+ char name[80]; | |
+ void __iomem *base; | |
+ size_t off; | |
+ size_t cnt; | |
+ size_t max_offset; | |
+ char *buf; | |
+ size_t buf_len; | |
+ struct list_head head; | |
+}; | |
+ | |
+struct debug_log { | |
+ struct dentry *xlog; | |
+ u32 xlog_enable; | |
+ u32 panic_on_err; | |
+ u32 enable_reg_dump; | |
+}; | |
+ | |
+struct mdss_debug_data { | |
+ struct dentry *root; | |
+ struct list_head base_list; | |
+ struct debug_log logd; | |
+}; | |
+ | |
int mdss_debugfs_init(struct mdss_data_type *mdata); | |
int mdss_debugfs_remove(struct mdss_data_type *mdata); | |
int mdss_debug_register_base(const char *name, void __iomem *base, | |
@@ -30,6 +73,13 @@ int mdss_misr_set(struct mdss_data_type *mdata, struct mdp_misr *req, | |
int mdss_misr_get(struct mdss_data_type *mdata, struct mdp_misr *resp, | |
struct mdss_mdp_ctl *ctl); | |
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id); | |
+ | |
+int mdss_create_xlog_debug(struct mdss_debug_data *mdd); | |
+void mdss_xlog(const char *name, ...); | |
+void mdss_xlog_dump(void); | |
+void mdss_dump_reg(char __iomem *base, int len); | |
+void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata); | |
+void mdss_xlog_tout_handler(const char *name, ...); | |
#else | |
static inline int mdss_debugfs_init(struct mdss_data_type *mdata) { return 0; } | |
static inline int mdss_debugfs_remove(struct mdss_data_type *mdata) | |
@@ -46,5 +96,12 @@ static inline int mdss_misr_get(struct mdss_data_type *mdata, | |
{ return 0; } | |
static inline void mdss_misr_crc_collect(struct mdss_data_type *mdata, | |
int block_id) { } | |
+ | |
+static inline int create_xlog_debug(struct mdss_data_type *mdata) { } | |
+static inline void mdss_xlog(const char *name, ...) { } | |
+static inline void mdss_xlog_dump(void) { } | |
+static inline void mdss_dump_reg(char __iomem *base, int len) { } | |
+static inline void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata) { } | |
+static inline void mdss_xlog_tout_handler(const char *name, ...) { } | |
#endif | |
#endif /* MDSS_DEBUG_H */ | |
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c | |
index e1d9a56..ee8994a 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi.c | |
+++ b/drivers/video/msm/mdss/mdss_dsi.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -21,17 +21,12 @@ | |
#include <linux/gpio.h> | |
#include <linux/err.h> | |
#include <linux/regulator/consumer.h> | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
-#include <mach/board_lge.h> | |
-#endif | |
#include "mdss.h" | |
#include "mdss_panel.h" | |
#include "mdss_dsi.h" | |
#include "mdss_debug.h" | |
-static unsigned char *mdss_dsi_base; | |
- | |
static int mdss_dsi_regulator_init(struct platform_device *pdev) | |
{ | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
@@ -67,6 +62,9 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata, int enable) | |
panel_data); | |
pr_debug("%s: enable=%d\n", __func__, enable); | |
+ if (pdata->panel_info.dynamic_switch_pending) | |
+ return 0; | |
+ | |
if (enable) { | |
ret = msm_dss_enable_vreg( | |
ctrl_pdata->power_data.vreg_config, | |
@@ -77,21 +75,25 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata, int enable) | |
goto error; | |
} | |
- if (pdata->panel_info.panel_power_on == 0) { | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- nt35521_panel_power(pdata,1); | |
-#endif | |
- mdss_dsi_panel_reset(pdata, 1); | |
+ if (!pdata->panel_info.mipi.lp11_init) { | |
+ ret = mdss_dsi_panel_reset(pdata, 1); | |
+ if (ret) { | |
+ pr_err("%s: Panel reset failed. rc=%d\n", | |
+ __func__, ret); | |
+ if (msm_dss_enable_vreg( | |
+ ctrl_pdata->power_data.vreg_config, | |
+ ctrl_pdata->power_data.num_vreg, 0)) | |
+ pr_err("Disable vregs failed\n"); | |
+ goto error; | |
+ } | |
} | |
- | |
} else { | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- mdelay(120); | |
-#endif | |
- mdss_dsi_panel_reset(pdata, 0); | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- nt35521_panel_power(pdata,0); | |
-#endif | |
+ ret = mdss_dsi_panel_reset(pdata, 0); | |
+ if (ret) { | |
+ pr_err("%s: Panel reset failed. rc=%d\n", | |
+ __func__, ret); | |
+ goto error; | |
+ } | |
ret = msm_dss_enable_vreg( | |
ctrl_pdata->power_data.vreg_config, | |
ctrl_pdata->power_data.num_vreg, 0); | |
@@ -99,9 +101,6 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata, int enable) | |
pr_err("%s: Failed to disable vregs.rc=%d\n", | |
__func__, ret); | |
} | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- mdelay(1); | |
-#endif | |
} | |
error: | |
return ret; | |
@@ -132,7 +131,7 @@ static int mdss_dsi_get_dt_vreg_data(struct device *dev, | |
if (!dev || !mp) { | |
pr_err("%s: invalid input\n", __func__); | |
rc = -EINVAL; | |
- goto error; | |
+ return rc; | |
} | |
of_node = dev->of_node; | |
@@ -312,7 +311,7 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata) | |
if (!pdata->panel_info.panel_power_on) { | |
pr_warn("%s:%d Panel already off.\n", __func__, __LINE__); | |
- return -EPERM; | |
+ return 0; | |
} | |
pdata->panel_info.panel_power_on = 0; | |
@@ -320,33 +319,25 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata) | |
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
panel_data); | |
+ mutex_lock(&ctrl_pdata->mutex); | |
panel_info = &ctrl_pdata->panel_data.panel_info; | |
pr_debug("%s+: ctrl=%p ndx=%d\n", __func__, | |
ctrl_pdata, ctrl_pdata->ndx); | |
if (pdata->panel_info.type == MIPI_CMD_PANEL) | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 1); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
/* disable DSI controller */ | |
mdss_dsi_controller_cfg(0, pdata); | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 0); | |
- | |
- ret = mdss_dsi_enable_bus_clocks(ctrl_pdata); | |
- if (ret) { | |
- pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__, | |
- ret); | |
- mdss_dsi_panel_power_on(pdata, 0); | |
- return ret; | |
- } | |
- | |
/* disable DSI phy */ | |
- mdss_dsi_phy_enable(ctrl_pdata, 0); | |
+ mdss_dsi_phy_disable(ctrl_pdata); | |
- mdss_dsi_disable_bus_clocks(ctrl_pdata); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
ret = mdss_dsi_panel_power_on(pdata, 0); | |
if (ret) { | |
+ mutex_unlock(&ctrl_pdata->mutex); | |
pr_err("%s: Panel power off failed\n", __func__); | |
return ret; | |
} | |
@@ -356,71 +347,28 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata) | |
&& (panel_info->new_fps != panel_info->mipi.frame_rate)) | |
panel_info->mipi.frame_rate = panel_info->new_fps; | |
+ mutex_unlock(&ctrl_pdata->mutex); | |
pr_debug("%s-:\n", __func__); | |
return ret; | |
} | |
-int mdss_dsi_on(struct mdss_panel_data *pdata) | |
+static void __mdss_dsi_ctrl_setup(struct mdss_panel_data *pdata) | |
{ | |
- int ret = 0; | |
- u32 clk_rate; | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
struct mdss_panel_info *pinfo; | |
struct mipi_panel_info *mipi; | |
+ u32 clk_rate; | |
u32 hbp, hfp, vbp, vfp, hspw, vspw, width, height; | |
u32 ystride, bpp, data, dst_bpp; | |
u32 dummy_xres, dummy_yres; | |
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
u32 hsync_period, vsync_period; | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- u32 tmp; | |
-#endif | |
- | |
- if (pdata == NULL) { | |
- pr_err("%s: Invalid input data\n", __func__); | |
- return -EINVAL; | |
- } | |
- | |
- if (pdata->panel_info.panel_power_on) { | |
- pr_warn("%s:%d Panel already on.\n", __func__, __LINE__); | |
- return 0; | |
- } | |
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
panel_data); | |
- pr_debug("%s+: ctrl=%p ndx=%d\n", | |
- __func__, ctrl_pdata, ctrl_pdata->ndx); | |
- | |
pinfo = &pdata->panel_info; | |
- ret = msm_dss_enable_vreg(ctrl_pdata->power_data.vreg_config, | |
- ctrl_pdata->power_data.num_vreg, 1); | |
- if (ret) { | |
- pr_err("%s:Failed to enable vregs. rc=%d\n", __func__, ret); | |
- return ret; | |
- } | |
- | |
- pdata->panel_info.panel_power_on = 1; | |
- | |
- if (!pdata->panel_info.mipi.lp11_init) | |
- mdss_dsi_panel_reset(pdata, 1); | |
- | |
- ret = mdss_dsi_enable_bus_clocks(ctrl_pdata); | |
- if (ret) { | |
- pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__, | |
- ret); | |
- mdss_dsi_panel_power_on(pdata, 0); | |
- pdata->panel_info.panel_power_on = 0; | |
- return ret; | |
- } | |
- | |
- mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base)); | |
- mdss_dsi_phy_init(pdata); | |
- mdss_dsi_disable_bus_clocks(ctrl_pdata); | |
- | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 1); | |
- | |
clk_rate = pdata->panel_info.clk_rate; | |
clk_rate = min(clk_rate, pdata->panel_info.clk_max); | |
@@ -450,7 +398,7 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) | |
vsync_period = vspw + vbp + height + dummy_yres + vfp; | |
hsync_period = hspw + hbp + width + dummy_xres + hfp; | |
- mipi = &pdata->panel_info.mipi; | |
+ mipi = &pdata->panel_info.mipi; | |
if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { | |
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x24, | |
((hspw + hbp + width + dummy_xres) << 16 | | |
@@ -488,34 +436,290 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) | |
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x64, data); | |
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x5C, data); | |
} | |
+} | |
- mdss_dsi_sw_reset(pdata); | |
- mdss_dsi_host_init(mipi, pdata); | |
+static inline bool __mdss_dsi_ulps_feature_enabled( | |
+ struct mdss_panel_data *pdata) | |
+{ | |
+ return pdata->panel_info.ulps_feature_enabled; | |
+} | |
+ | |
+static int mdss_dsi_ulps_config_sub(struct mdss_dsi_ctrl_pdata *ctrl_pdata, | |
+ int enable) | |
+{ | |
+ int ret = 0; | |
+ struct mdss_panel_data *pdata = NULL; | |
+ struct mipi_panel_info *pinfo = NULL; | |
+ u32 lane_status = 0; | |
+ u32 active_lanes = 0; | |
+ | |
+ if (!ctrl_pdata) { | |
+ pr_err("%s: invalid input\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ pdata = &ctrl_pdata->panel_data; | |
+ if (!pdata) { | |
+ pr_err("%s: Invalid panel data\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ pinfo = &pdata->panel_info.mipi; | |
+ | |
+ if (!__mdss_dsi_ulps_feature_enabled(pdata)) { | |
+ pr_debug("%s: ULPS feature not supported. enable=%d\n", | |
+ __func__, enable); | |
+ return -ENOTSUPP; | |
+ } | |
+ | |
+ if (enable && !ctrl_pdata->ulps) { | |
+ /* No need to configure ULPS mode when entering suspend state */ | |
+ if (!pdata->panel_info.panel_power_on) { | |
+ pr_err("%s: panel off. returning\n", __func__); | |
+ goto error; | |
+ } | |
+ | |
+ if (__mdss_dsi_clk_enabled(ctrl_pdata, DSI_LINK_CLKS)) { | |
+ pr_err("%s: cannot enter ulps mode if dsi clocks are on\n", | |
+ __func__); | |
+ ret = -EPERM; | |
+ goto error; | |
+ } | |
+ | |
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
+ if (ret) { | |
+ pr_err("%s: Failed to enable clocks. rc=%d\n", | |
+ __func__, ret); | |
+ goto error; | |
+ } | |
+ | |
+ /* | |
+ * ULPS Entry Request. | |
+ * Wait for a short duration to ensure that the lanes | |
+ * enter ULP state. | |
+ */ | |
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F); | |
+ usleep(100); | |
+ | |
+ /* Check to make sure that all active data lanes are in ULPS */ | |
+ if (pinfo->data_lane3) | |
+ active_lanes |= BIT(11); | |
+ if (pinfo->data_lane2) | |
+ active_lanes |= BIT(10); | |
+ if (pinfo->data_lane1) | |
+ active_lanes |= BIT(9); | |
+ if (pinfo->data_lane0) | |
+ active_lanes |= BIT(8); | |
+ active_lanes |= BIT(12); /* clock lane */ | |
+ lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8); | |
+ if (lane_status & active_lanes) { | |
+ pr_err("%s: ULPS entry req failed. Lane status=0x%08x\n", | |
+ __func__, lane_status); | |
+ ret = -EINVAL; | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
+ goto error; | |
+ } | |
+ | |
+ /* Enable MMSS DSI Clamps */ | |
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF); | |
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x83FF); | |
+ | |
+ wmb(); | |
+ | |
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x1); | |
+ /* disable DSI controller */ | |
+ mdss_dsi_controller_cfg(0, pdata); | |
+ | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
+ ctrl_pdata->ulps = true; | |
+ } else if (ctrl_pdata->ulps) { | |
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 1); | |
+ if (ret) { | |
+ pr_err("%s: Failed to enable bus clocks. rc=%d\n", | |
+ __func__, ret); | |
+ goto error; | |
+ } | |
+ | |
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x108, 0x0); | |
+ mdss_dsi_phy_init(pdata); | |
+ | |
+ __mdss_dsi_ctrl_setup(pdata); | |
+ mdss_dsi_sw_reset(pdata); | |
+ mdss_dsi_host_init(pdata); | |
+ mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, | |
+ pdata); | |
+ | |
+ /* | |
+ * ULPS Entry Request. This is needed because, after power | |
+ * collapse and reset, the DSI controller resets back to | |
+ * idle state and not ULPS. | |
+ * Wait for a short duration to ensure that the lanes | |
+ * enter ULP state. | |
+ */ | |
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x01F); | |
+ usleep(100); | |
+ | |
+ /* Disable MMSS DSI Clamps */ | |
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x3FF); | |
+ MIPI_OUTP(ctrl_pdata->mmss_misc_io.base + 0x14, 0x0); | |
+ | |
+ ret = mdss_dsi_clk_ctrl(ctrl_pdata, DSI_LINK_CLKS, 1); | |
+ if (ret) { | |
+ pr_err("%s: Failed to enable link clocks. rc=%d\n", | |
+ __func__, ret); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0); | |
+ goto error; | |
+ } | |
+ | |
+ /* | |
+ * ULPS Exit Request | |
+ * Hardware requirement is to wait for at least 1ms | |
+ */ | |
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x1F00); | |
+ usleep(1000); | |
+ MIPI_OUTP(ctrl_pdata->ctrl_base + 0x0AC, 0x0); | |
+ | |
+ /* | |
+ * Wait for a short duration before enabling | |
+ * data transmission | |
+ */ | |
+ usleep(100); | |
+ | |
+ lane_status = MIPI_INP(ctrl_pdata->ctrl_base + 0xA8); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_LINK_CLKS, 0); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0); | |
+ ctrl_pdata->ulps = false; | |
+ } | |
+ | |
+ pr_debug("%s: DSI lane status = 0x%08x. Ulps %s\n", __func__, | |
+ lane_status, enable ? "enabled" : "disabled"); | |
+ | |
+error: | |
+ return ret; | |
+} | |
+ | |
+static int mdss_dsi_update_panel_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata, | |
+ int mode) | |
+{ | |
+ int ret = 0; | |
+ struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); | |
+ | |
+ if (mode == DSI_CMD_MODE) { | |
+ pinfo->mipi.mode = DSI_CMD_MODE; | |
+ pinfo->type = MIPI_CMD_PANEL; | |
+ pinfo->mipi.vsync_enable = 1; | |
+ pinfo->mipi.hw_vsync_mode = 1; | |
+ } else { /*video mode*/ | |
+ pinfo->mipi.mode = DSI_VIDEO_MODE; | |
+ pinfo->type = MIPI_VIDEO_PANEL; | |
+ pinfo->mipi.vsync_enable = 0; | |
+ pinfo->mipi.hw_vsync_mode = 0; | |
+ } | |
+ | |
+ ctrl_pdata->panel_mode = pinfo->mipi.mode; | |
+ mdss_panel_get_dst_fmt(pinfo->bpp, pinfo->mipi.mode, | |
+ pinfo->mipi.pixel_packing, &(pinfo->mipi.dst_format)); | |
+ pinfo->cont_splash_enabled = 0; | |
+ | |
+ return ret; | |
+} | |
+static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, | |
+ int enable) | |
+{ | |
+ int rc; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
+ | |
+ if (&ctrl->mmss_misc_io == NULL) { | |
+ pr_err("%s: mmss_misc_io is NULL. ULPS not valid\n", __func__); | |
+ return -EINVAL; | |
+ } | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- if(nt35521_panel_power(pdata, 1)){ | |
- pr_err("%s:Failed to disable lge_asus_panel_power.rc\n",__func__); | |
+ if (mdss_dsi_is_slave_ctrl(ctrl)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (!mctrl) { | |
+ pr_err("%s: Unable to get master control\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ } | |
+ | |
+ if (mctrl) { | |
+ pr_debug("%s: configuring ulps (%s) for master ctrl%d\n", | |
+ __func__, (enable ? "on" : "off"), ctrl->ndx); | |
+ rc = mdss_dsi_ulps_config_sub(mctrl, enable); | |
+ if (rc) | |
+ return rc; | |
+ } | |
+ | |
+ pr_debug("%s: configuring ulps (%s) for ctrl%d\n", | |
+ __func__, (enable ? "on" : "off"), ctrl->ndx); | |
+ return mdss_dsi_ulps_config_sub(ctrl, enable); | |
+} | |
+ | |
+int mdss_dsi_on(struct mdss_panel_data *pdata) | |
+{ | |
+ int ret = 0; | |
+ struct mdss_panel_info *pinfo; | |
+ struct mipi_panel_info *mipi; | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ | |
+ if (pdata == NULL) { | |
+ pr_err("%s: Invalid input data\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (pdata->panel_info.panel_power_on) { | |
+ pr_warn("%s:%d Panel already on.\n", __func__, __LINE__); | |
return 0; | |
} | |
- mipi->force_clk_lane_hs = 1; | |
- mdelay(5); | |
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
+ panel_data); | |
+ | |
+ pr_debug("%s+: ctrl=%p ndx=%d\n", | |
+ __func__, ctrl_pdata, ctrl_pdata->ndx); | |
+ | |
+ pinfo = &pdata->panel_info; | |
+ mipi = &pdata->panel_info.mipi; | |
+ | |
+ ret = mdss_dsi_panel_power_on(pdata, 1); | |
+ if (ret) { | |
+ pr_err("%s:Panel power on failed. rc=%d\n", __func__, ret); | |
+ return ret; | |
+ } | |
+ | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 1); | |
+ if (ret) { | |
+ pr_err("%s: failed to enable bus clocks. rc=%d\n", __func__, | |
+ ret); | |
+ ret = mdss_dsi_panel_power_on(pdata, 0); | |
+ if (ret) { | |
+ pr_err("%s: Panel reset failed. rc=%d\n", | |
+ __func__, ret); | |
+ return ret; | |
+ } | |
+ pdata->panel_info.panel_power_on = 0; | |
+ return ret; | |
+ } | |
+ pdata->panel_info.panel_power_on = 1; | |
+ | |
+ mdss_dsi_phy_sw_reset((ctrl_pdata->ctrl_base)); | |
+ mdss_dsi_phy_init(pdata); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_BUS_CLKS, 0); | |
- tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac); | |
- tmp &= ~(1<<28); | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp); | |
- wmb(); | |
-#endif | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
+ | |
+ __mdss_dsi_ctrl_setup(pdata); | |
+ mdss_dsi_sw_reset(pdata); | |
+ mdss_dsi_host_init(pdata); | |
/* | |
* Issue hardware reset line after enabling the DSI clocks and data | |
* data lanes for LP11 init | |
*/ | |
- if (pdata->panel_info.mipi.lp11_init) | |
+ if (mipi->lp11_init) | |
mdss_dsi_panel_reset(pdata, 1); | |
- if (pdata->panel_info.mipi.init_delay) | |
- usleep(pdata->panel_info.mipi.init_delay); | |
+ if (mipi->init_delay) | |
+ usleep(mipi->init_delay); | |
if (mipi->force_clk_lane_hs) { | |
u32 tmp; | |
@@ -527,7 +731,7 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) | |
} | |
if (pdata->panel_info.type == MIPI_CMD_PANEL) | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 0); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
pr_debug("%s-:\n", __func__); | |
return 0; | |
@@ -551,11 +755,13 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata) | |
mipi = &pdata->panel_info.mipi; | |
if (!(ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)) { | |
- ret = ctrl_pdata->on(pdata); | |
- if (ret) { | |
- pr_err("%s: unable to initialize the panel\n", | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ ret = ctrl_pdata->on(pdata); | |
+ if (ret) { | |
+ pr_err("%s: unable to initialize the panel\n", | |
__func__); | |
- return ret; | |
+ return ret; | |
+ } | |
} | |
ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT; | |
} | |
@@ -589,8 +795,36 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata) | |
panel_data); | |
mipi = &pdata->panel_info.mipi; | |
+ if (__mdss_dsi_ulps_feature_enabled(pdata) && | |
+ (ctrl_pdata->ulps)) { | |
+ /* Disable ULPS mode before blanking the panel */ | |
+ ret = mdss_dsi_ulps_config(ctrl_pdata, 0); | |
+ if (ret) { | |
+ pr_err("%s: failed to exit ULPS mode. rc=%d\n", | |
+ __func__, ret); | |
+ return ret; | |
+ } | |
+ } | |
+ | |
+ if (pdata->panel_info.type == MIPI_VIDEO_PANEL && | |
+ ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) { | |
+ mdss_dsi_sw_reset(pdata); | |
+ mdss_dsi_host_init(pdata); | |
+ } | |
+ | |
mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata); | |
+ if (pdata->panel_info.dynamic_switch_pending) { | |
+ pr_info("%s: switching to %s mode\n", __func__, | |
+ (pdata->panel_info.mipi.mode ? "video" : "command")); | |
+ if (pdata->panel_info.type == MIPI_CMD_PANEL) { | |
+ ctrl_pdata->switch_mode(pdata, DSI_VIDEO_MODE); | |
+ } else if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { | |
+ ctrl_pdata->switch_mode(pdata, DSI_CMD_MODE); | |
+ mdss_dsi_set_tear_off(ctrl_pdata); | |
+ } | |
+ } | |
+ | |
if (pdata->panel_info.type == MIPI_CMD_PANEL) { | |
if (mipi->vsync_enable && mipi->hw_vsync_mode | |
&& gpio_is_valid(ctrl_pdata->disp_te_gpio)) { | |
@@ -599,10 +833,12 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata) | |
} | |
if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { | |
- ret = ctrl_pdata->off(pdata); | |
- if (ret) { | |
- pr_err("%s: Panel OFF failed\n", __func__); | |
- return ret; | |
+ if (!pdata->panel_info.dynamic_switch_pending) { | |
+ ret = ctrl_pdata->off(pdata); | |
+ if (ret) { | |
+ pr_err("%s: Panel OFF failed\n", __func__); | |
+ return ret; | |
+ } | |
} | |
ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; | |
} | |
@@ -635,17 +871,8 @@ int mdss_dsi_cont_splash_on(struct mdss_panel_data *pdata) | |
"Incorrect Ctrl state=0x%x\n", ctrl_pdata->ctrl_state); | |
mdss_dsi_sw_reset(pdata); | |
- mdss_dsi_host_init(mipi, pdata); | |
+ mdss_dsi_host_init(pdata); | |
mdss_dsi_op_mode_config(mipi->mode, pdata); | |
- | |
- if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) { | |
- ret = mdss_dsi_unblank(pdata); | |
- if (ret) { | |
- pr_err("%s: unblank failed\n", __func__); | |
- return ret; | |
- } | |
- } | |
- | |
pr_debug("%s-:End\n", __func__); | |
return ret; | |
} | |
@@ -674,33 +901,58 @@ static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps) | |
if (new_fps != | |
ctrl_pdata->panel_data.panel_info.mipi.frame_rate) { | |
- rc = mdss_dsi_clk_div_config | |
- (&ctrl_pdata->panel_data.panel_info, new_fps); | |
- if (rc) { | |
- pr_err("%s: unable to initialize the clk dividers\n", | |
- __func__); | |
- return rc; | |
- } | |
- ctrl_pdata->pclk_rate = | |
- ctrl_pdata->panel_data.panel_info.mipi.dsi_pclk_rate; | |
- ctrl_pdata->byte_clk_rate = | |
- ctrl_pdata->panel_data.panel_info.clk_rate / 8; | |
- | |
if (pdata->panel_info.dfps_update | |
- == DFPS_IMMEDIATE_CLK_UPDATE_MODE) { | |
- dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + | |
- 0x0004); | |
- ctrl_pdata->panel_data.panel_info.mipi.frame_rate = | |
- new_fps; | |
- dsi_ctrl &= ~0x2; | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, | |
- dsi_ctrl); | |
- mdss_dsi_controller_cfg(true, pdata); | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 0); | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 1); | |
- dsi_ctrl |= 0x2; | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, | |
- dsi_ctrl); | |
+ == DFPS_IMMEDIATE_PORCH_UPDATE_MODE) { | |
+ u32 hsync_period, vsync_period; | |
+ u32 new_dsi_v_total, current_dsi_v_total; | |
+ vsync_period = | |
+ mdss_panel_get_vtotal(&pdata->panel_info); | |
+ hsync_period = | |
+ mdss_panel_get_htotal(&pdata->panel_info); | |
+ current_dsi_v_total = | |
+ MIPI_INP((ctrl_pdata->ctrl_base) + 0x2C); | |
+ new_dsi_v_total = | |
+ ((vsync_period - 1) << 16) | (hsync_period - 1); | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, | |
+ (current_dsi_v_total | 0x8000000)); | |
+ if (new_dsi_v_total & 0x8000000) { | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, | |
+ new_dsi_v_total); | |
+ } else { | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, | |
+ (new_dsi_v_total | 0x8000000)); | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2C, | |
+ (new_dsi_v_total & 0x7ffffff)); | |
+ } | |
+ pdata->panel_info.mipi.frame_rate = new_fps; | |
+ } else { | |
+ rc = mdss_dsi_clk_div_config | |
+ (&ctrl_pdata->panel_data.panel_info, new_fps); | |
+ if (rc) { | |
+ pr_err("%s: unable to initialize the clk dividers\n", | |
+ __func__); | |
+ return rc; | |
+ } | |
+ ctrl_pdata->pclk_rate = | |
+ pdata->panel_info.mipi.dsi_pclk_rate; | |
+ ctrl_pdata->byte_clk_rate = | |
+ pdata->panel_info.clk_rate / 8; | |
+ | |
+ if (pdata->panel_info.dfps_update | |
+ == DFPS_IMMEDIATE_CLK_UPDATE_MODE) { | |
+ dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + | |
+ 0x0004); | |
+ pdata->panel_info.mipi.frame_rate = new_fps; | |
+ dsi_ctrl &= ~0x2; | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, | |
+ dsi_ctrl); | |
+ mdss_dsi_controller_cfg(true, pdata); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
+ dsi_ctrl |= 0x2; | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, | |
+ dsi_ctrl); | |
+ } | |
} | |
} else { | |
pr_debug("%s: Panel is already at this FPS\n", __func__); | |
@@ -746,6 +998,15 @@ static int mdss_dsi_ctl_partial_update(struct mdss_panel_data *pdata) | |
return rc; | |
} | |
+int mdss_dsi_register_recovery_handler(struct mdss_dsi_ctrl_pdata *ctrl, | |
+ struct mdss_panel_recovery *recovery) | |
+{ | |
+ mutex_lock(&ctrl->mutex); | |
+ ctrl->recovery = recovery; | |
+ mutex_unlock(&ctrl->mutex); | |
+ return 0; | |
+} | |
+ | |
static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, | |
int event, void *arg) | |
{ | |
@@ -760,6 +1021,8 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, | |
panel_data); | |
pr_debug("%s+:event=%d\n", __func__, event); | |
+ MDSS_XLOG(event, arg, ctrl_pdata->ndx, 0x3333); | |
+ | |
switch (event) { | |
case MDSS_EVENT_UNBLANK: | |
rc = mdss_dsi_on(pdata); | |
@@ -784,21 +1047,15 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, | |
rc = mdss_dsi_off(pdata); | |
break; | |
case MDSS_EVENT_CONT_SPLASH_FINISH: | |
+ if (ctrl_pdata->off_cmds.link_state == DSI_LP_MODE) | |
+ rc = mdss_dsi_blank(pdata); | |
ctrl_pdata->ctrl_state &= ~CTRL_STATE_MDP_ACTIVE; | |
- if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) { | |
- rc = mdss_dsi_cont_splash_on(pdata); | |
- } else { | |
- pr_debug("%s:event=%d, Dsi On not called: ctrl_state: %d\n", | |
- __func__, event, | |
- ctrl_pdata->on_cmds.link_state); | |
- rc = -EINVAL; | |
- } | |
+ rc = mdss_dsi_cont_splash_on(pdata); | |
break; | |
case MDSS_EVENT_PANEL_CLK_CTRL: | |
mdss_dsi_clk_req(ctrl_pdata, (int)arg); | |
break; | |
case MDSS_EVENT_DSI_CMDLIST_KOFF: | |
- ctrl_pdata->recovery = (struct mdss_panel_recovery *)arg; | |
mdss_dsi_cmdlist_commit(ctrl_pdata, 1); | |
break; | |
case MDSS_EVENT_PANEL_UPDATE_FPS: | |
@@ -817,6 +1074,17 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, | |
case MDSS_EVENT_ENABLE_PARTIAL_UPDATE: | |
rc = mdss_dsi_ctl_partial_update(pdata); | |
break; | |
+ case MDSS_EVENT_DSI_ULPS_CTRL: | |
+ rc = mdss_dsi_ulps_config(ctrl_pdata, (int)arg); | |
+ break; | |
+ case MDSS_EVENT_REGISTER_RECOVERY_HANDLER: | |
+ rc = mdss_dsi_register_recovery_handler(ctrl_pdata, | |
+ (struct mdss_panel_recovery *)arg); | |
+ break; | |
+ case MDSS_EVENT_DSI_DYNAMIC_SWITCH: | |
+ rc = mdss_dsi_update_panel_config(ctrl_pdata, | |
+ (int)(unsigned long) arg); | |
+ break; | |
default: | |
pr_debug("%s: unhandled event=%d\n", __func__, event); | |
break; | |
@@ -857,35 +1125,39 @@ static struct device_node *mdss_dsi_pref_prim_panel( | |
static struct device_node *mdss_dsi_find_panel_of_node( | |
struct platform_device *pdev, char *panel_cfg) | |
{ | |
- int l; | |
- int ctrl_id = -1; | |
- char *panel_name; | |
+ int len, i; | |
+ int ctrl_id = pdev->id - 1; | |
+ char panel_name[MDSS_MAX_PANEL_LEN]; | |
+ char ctrl_id_stream[3] = "0:"; | |
+ char *stream = NULL, *pan = NULL; | |
struct device_node *dsi_pan_node = NULL, *mdss_node = NULL; | |
- l = strlen(panel_cfg); | |
- if (!l) { | |
+ len = strlen(panel_cfg); | |
+ if (!len) { | |
/* no panel cfg chg, parse dt */ | |
pr_debug("%s:%d: no cmd line cfg present\n", | |
__func__, __LINE__); | |
- dsi_pan_node = mdss_dsi_pref_prim_panel(pdev); | |
+ goto end; | |
} else { | |
- if (panel_cfg[0] == '0') { | |
- pr_debug("%s:%d: DSI ctrl 1\n", __func__, __LINE__); | |
- ctrl_id = 0; | |
- } else if (panel_cfg[0] == '1') { | |
- pr_debug("%s:%d: DSI ctrl 2\n", __func__, __LINE__); | |
- ctrl_id = 1; | |
+ if (ctrl_id == 1) | |
+ strlcpy(ctrl_id_stream, "1:", 3); | |
+ | |
+ stream = strnstr(panel_cfg, ctrl_id_stream, len); | |
+ if (!stream) { | |
+ pr_err("controller config is not present\n"); | |
+ goto end; | |
} | |
- if ((pdev->id - 1) != ctrl_id) { | |
- pr_err("%s:%d:pdev_ID=[%d]\n", | |
- __func__, __LINE__, pdev->id); | |
- return NULL; | |
+ stream += 2; | |
+ | |
+ pan = strnchr(stream, strlen(stream), ':'); | |
+ if (!pan) { | |
+ strlcpy(panel_name, stream, MDSS_MAX_PANEL_LEN); | |
+ } else { | |
+ for (i = 0; (stream + i) < pan; i++) | |
+ panel_name[i] = *(stream + i); | |
+ panel_name[i] = 0; | |
} | |
- /* | |
- * skip first two chars '<dsi_ctrl_id>' and | |
- * ':' to get to the panel name | |
- */ | |
- panel_name = panel_cfg + 2; | |
+ | |
pr_debug("%s:%d:%s:%s\n", __func__, __LINE__, | |
panel_cfg, panel_name); | |
@@ -902,9 +1174,12 @@ static struct device_node *mdss_dsi_find_panel_of_node( | |
if (!dsi_pan_node) { | |
pr_err("%s: invalid pan node, selecting prim panel\n", | |
__func__); | |
- dsi_pan_node = mdss_dsi_pref_prim_panel(pdev); | |
+ goto end; | |
} | |
+ return dsi_pan_node; | |
} | |
+end: | |
+ dsi_pan_node = mdss_dsi_pref_prim_panel(pdev); | |
return dsi_pan_node; | |
} | |
@@ -916,7 +1191,6 @@ static int __devinit mdss_dsi_ctrl_probe(struct platform_device *pdev) | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
struct device_node *dsi_pan_node = NULL; | |
char panel_cfg[MDSS_MAX_PANEL_LEN]; | |
- struct resource *mdss_dsi_mres; | |
const char *ctrl_name; | |
bool cmd_cfg_cont_splash = true; | |
@@ -966,30 +1240,13 @@ static int __devinit mdss_dsi_ctrl_probe(struct platform_device *pdev) | |
else | |
pdev->id = 2; | |
- mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
- if (!mdss_dsi_mres) { | |
- pr_err("%s:%d unable to get the MDSS resources", | |
- __func__, __LINE__); | |
- rc = -ENOMEM; | |
- goto error_no_mem; | |
- } | |
- | |
- mdss_dsi_base = ioremap(mdss_dsi_mres->start, | |
- resource_size(mdss_dsi_mres)); | |
- if (!mdss_dsi_base) { | |
- pr_err("%s:%d unable to remap dsi resources", | |
- __func__, __LINE__); | |
- rc = -ENOMEM; | |
- goto error_no_mem; | |
- } | |
- | |
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_ioremap; | |
+ goto error_no_mem; | |
} | |
/* Parse the regulator information */ | |
@@ -1036,8 +1293,6 @@ error_pan_node: | |
of_node_put(dsi_pan_node); | |
error_vreg: | |
mdss_dsi_put_dt_vreg_data(&pdev->dev, &ctrl_pdata->power_data); | |
-error_ioremap: | |
- iounmap(mdss_dsi_base); | |
error_no_mem: | |
devm_kfree(&pdev->dev, ctrl_pdata); | |
@@ -1060,7 +1315,9 @@ static int __devexit mdss_dsi_ctrl_remove(struct platform_device *pdev) | |
pr_err("%s: failed to de-init vregs\n", __func__); | |
mdss_dsi_put_dt_vreg_data(&pdev->dev, &ctrl_pdata->power_data); | |
mfd = platform_get_drvdata(pdev); | |
- iounmap(mdss_dsi_base); | |
+ msm_dss_iounmap(&ctrl_pdata->mmss_misc_io); | |
+ msm_dss_iounmap(&ctrl_pdata->phy_io); | |
+ msm_dss_iounmap(&ctrl_pdata->ctrl_io); | |
return 0; | |
} | |
@@ -1071,7 +1328,6 @@ int mdss_dsi_retrieve_ctrl_resources(struct platform_device *pdev, int mode, | |
{ | |
int rc = 0; | |
u32 index; | |
- struct resource *mdss_dsi_mres; | |
rc = of_property_read_u32(pdev->dev.of_node, "cell-index", &index); | |
if (rc) { | |
@@ -1099,25 +1355,33 @@ int mdss_dsi_retrieve_ctrl_resources(struct platform_device *pdev, int mode, | |
return -EPERM; | |
} | |
- mdss_dsi_mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
- if (!mdss_dsi_mres) { | |
- pr_err("%s:%d unable to get the DSI ctrl resources", | |
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->ctrl_io, "dsi_ctrl"); | |
+ if (rc) { | |
+ pr_err("%s:%d unable to remap dsi ctrl resources", | |
__func__, __LINE__); | |
- return -ENOMEM; | |
+ return rc; | |
} | |
- ctrl->ctrl_base = ioremap(mdss_dsi_mres->start, | |
- resource_size(mdss_dsi_mres)); | |
- if (!(ctrl->ctrl_base)) { | |
- pr_err("%s:%d unable to remap dsi resources", | |
+ ctrl->ctrl_base = ctrl->ctrl_io.base; | |
+ ctrl->reg_size = ctrl->ctrl_io.len; | |
+ | |
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->phy_io, "dsi_phy"); | |
+ if (rc) { | |
+ pr_err("%s:%d unable to remap dsi phy resources", | |
__func__, __LINE__); | |
- return -ENOMEM; | |
+ return rc; | |
} | |
- ctrl->reg_size = resource_size(mdss_dsi_mres); | |
+ pr_info("%s: ctrl_base=%p ctrl_size=%x phy_base=%p phy_size=%x\n", | |
+ __func__, ctrl->ctrl_base, ctrl->reg_size, ctrl->phy_io.base, | |
+ ctrl->phy_io.len); | |
- pr_info("%s: dsi base=%x size=%x\n", | |
- __func__, (int)ctrl->ctrl_base, ctrl->reg_size); | |
+ rc = msm_dss_ioremap_byname(pdev, &ctrl->mmss_misc_io, | |
+ "mmss_misc_phys"); | |
+ if (rc) { | |
+ pr_debug("%s:%d mmss_misc IO remap failed\n", | |
+ __func__, __LINE__); | |
+ } | |
return 0; | |
} | |
@@ -1228,6 +1492,12 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
DFPS_IMMEDIATE_CLK_UPDATE_MODE; | |
pr_debug("%s: dfps mode: Immediate clk\n", | |
__func__); | |
+ } else if (!strcmp(data, | |
+ "dfps_immediate_porch_mode")) { | |
+ pinfo->dfps_update = | |
+ DFPS_IMMEDIATE_PORCH_UPDATE_MODE; | |
+ pr_debug("%s: dfps mode: Immediate porch\n", | |
+ __func__); | |
} else { | |
pr_debug("%s: dfps to default mode\n", | |
__func__); | |
@@ -1247,21 +1517,14 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
pinfo->new_fps = pinfo->mipi.frame_rate; | |
} | |
+ pinfo->panel_max_fps = mdss_panel_get_framerate(pinfo); | |
+ pinfo->panel_max_vtotal = mdss_panel_get_vtotal(pinfo); | |
ctrl_pdata->disp_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, | |
"qcom,platform-enable-gpio", 0); | |
- if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) { | |
+ if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
pr_err("%s:%d, Disp_en gpio not specified\n", | |
__func__, __LINE__); | |
- } else { | |
- rc = gpio_request(ctrl_pdata->disp_en_gpio, "disp_enable"); | |
- if (rc) { | |
- pr_err("request reset gpio failed, rc=%d\n", | |
- rc); | |
- gpio_free(ctrl_pdata->disp_en_gpio); | |
- return -ENODEV; | |
- } | |
- } | |
if (pinfo->type == MIPI_CMD_PANEL) { | |
ctrl_pdata->disp_te_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, | |
@@ -1278,7 +1541,6 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
if (rc) { | |
pr_err("request TE gpio failed, rc=%d\n", | |
rc); | |
- gpio_free(ctrl_pdata->disp_te_gpio); | |
return -ENODEV; | |
} | |
rc = gpio_tlmm_config(GPIO_CFG( | |
@@ -1308,44 +1570,20 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, | |
"qcom,platform-reset-gpio", 0); | |
- if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { | |
+ if (!gpio_is_valid(ctrl_pdata->rst_gpio)) | |
pr_err("%s:%d, reset gpio not specified\n", | |
__func__, __LINE__); | |
- } else { | |
- rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); | |
- if (rc) { | |
- pr_err("request reset gpio failed, rc=%d\n", | |
- rc); | |
- gpio_free(ctrl_pdata->rst_gpio); | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
- gpio_free(ctrl_pdata->disp_en_gpio); | |
- return -ENODEV; | |
- } | |
- } | |
if (pinfo->mode_gpio_state != MODE_GPIO_NOT_VALID) { | |
ctrl_pdata->mode_gpio = of_get_named_gpio( | |
ctrl_pdev->dev.of_node, | |
"qcom,platform-mode-gpio", 0); | |
- if (!gpio_is_valid(ctrl_pdata->mode_gpio)) { | |
+ if (!gpio_is_valid(ctrl_pdata->mode_gpio)) | |
pr_info("%s:%d, mode gpio not specified\n", | |
__func__, __LINE__); | |
- } else { | |
- rc = gpio_request(ctrl_pdata->mode_gpio, "panel_mode"); | |
- if (rc) { | |
- pr_err("request panel mode gpio failed,rc=%d\n", | |
- rc); | |
- gpio_free(ctrl_pdata->mode_gpio); | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
- gpio_free(ctrl_pdata->disp_en_gpio); | |
- if (gpio_is_valid(ctrl_pdata->rst_gpio)) | |
- gpio_free(ctrl_pdata->rst_gpio); | |
- if (gpio_is_valid(ctrl_pdata->disp_te_gpio)) | |
- gpio_free(ctrl_pdata->disp_te_gpio); | |
- return -ENODEV; | |
- } | |
- } | |
+ } else { | |
+ ctrl_pdata->mode_gpio = -EINVAL; | |
} | |
if (mdss_dsi_clk_init(ctrl_pdev, ctrl_pdata)) { | |
@@ -1361,8 +1599,16 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
} | |
ctrl_pdata->panel_data.event_handler = mdss_dsi_event_handler; | |
- ctrl_pdata->check_status = mdss_dsi_bta_status_check; | |
+ if (ctrl_pdata->status_mode == ESD_REG) | |
+ ctrl_pdata->check_status = mdss_dsi_reg_status_check; | |
+ else if (ctrl_pdata->status_mode == ESD_BTA) | |
+ ctrl_pdata->check_status = mdss_dsi_bta_status_check; | |
+ | |
+ if (ctrl_pdata->status_mode == ESD_MAX) { | |
+ pr_err("%s: Using default BTA for ESD check\n", __func__); | |
+ ctrl_pdata->check_status = mdss_dsi_bta_status_check; | |
+ } | |
if (ctrl_pdata->bklt_ctrl == BL_PWM) | |
mdss_dsi_panel_pwm_cfg(ctrl_pdata); | |
@@ -1386,7 +1632,7 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
return rc; | |
} | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 1); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
ctrl_pdata->ctrl_state |= | |
(CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE); | |
} else { | |
@@ -1396,10 +1642,6 @@ int dsi_panel_device_register(struct device_node *pan_node, | |
rc = mdss_register_panel(ctrl_pdev, &(ctrl_pdata->panel_data)); | |
if (rc) { | |
pr_err("%s: unable to register MIPI DSI panel\n", __func__); | |
- if (ctrl_pdata->rst_gpio) | |
- gpio_free(ctrl_pdata->rst_gpio); | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
- gpio_free(ctrl_pdata->disp_en_gpio); | |
return rc; | |
} | |
@@ -1454,7 +1696,6 @@ module_init(mdss_dsi_driver_init); | |
static void __exit mdss_dsi_driver_cleanup(void) | |
{ | |
- iounmap(mdss_dsi_base); | |
platform_driver_unregister(&mdss_dsi_ctrl_driver); | |
} | |
module_exit(mdss_dsi_driver_cleanup); | |
diff --git a/drivers/video/msm/mdss/mdss_dsi.h b/drivers/video/msm/mdss/mdss_dsi.h | |
index 7f7b03e..53ae680 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi.h | |
+++ b/drivers/video/msm/mdss/mdss_dsi.h | |
@@ -85,6 +85,12 @@ enum dsi_panel_bl_ctrl { | |
UNKNOWN_CTRL, | |
}; | |
+enum dsi_panel_status_mode { | |
+ ESD_BTA, | |
+ ESD_REG, | |
+ ESD_MAX, | |
+}; | |
+ | |
enum dsi_ctrl_op_mode { | |
DSI_LP_MODE, | |
DSI_HS_MODE, | |
@@ -151,8 +157,8 @@ enum dsi_lane_map_type { | |
#define DSI_CMD_TERM BIT(0) | |
extern struct device dsi_dev; | |
-extern int mdss_dsi_clk_on; | |
extern u32 dsi_irq; | |
+extern struct mdss_dsi_ctrl_pdata *ctrl_list[]; | |
struct dsiphy_pll_divider_config { | |
u32 clk_rate; | |
@@ -223,8 +229,17 @@ enum { | |
DSI_CTRL_MAX, | |
}; | |
+/* DSI controller #0 is always treated as a master in broadcast mode */ | |
+#define DSI_CTRL_MASTER DSI_CTRL_0 | |
+#define DSI_CTRL_SLAVE DSI_CTRL_1 | |
+ | |
+#define DSI_BUS_CLKS BIT(0) | |
+#define DSI_LINK_CLKS BIT(1) | |
+#define DSI_ALL_CLKS ((DSI_BUS_CLKS) | (DSI_LINK_CLKS)) | |
+ | |
#define DSI_EV_PLL_UNLOCKED 0x0001 | |
#define DSI_EV_MDP_FIFO_UNDERFLOW 0x0002 | |
+#define DSI_EV_DSI_FIFO_EMPTY 0x0003 | |
#define DSI_EV_MDP_BUSY_RELEASE 0x80000000 | |
struct mdss_dsi_ctrl_pdata { | |
@@ -234,28 +249,31 @@ struct mdss_dsi_ctrl_pdata { | |
int (*partial_update_fnc) (struct mdss_panel_data *pdata); | |
int (*check_status) (struct mdss_dsi_ctrl_pdata *pdata); | |
int (*cmdlist_commit)(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp); | |
+ void (*switch_mode) (struct mdss_panel_data *pdata, int mode); | |
struct mdss_panel_data panel_data; | |
unsigned char *ctrl_base; | |
+ struct dss_io_data ctrl_io; | |
+ struct dss_io_data mmss_misc_io; | |
+ struct dss_io_data phy_io; | |
int reg_size; | |
- u32 clk_cnt; | |
+ u32 bus_clk_cnt; | |
+ u32 link_clk_cnt; | |
+ u32 flags; | |
struct clk *mdp_core_clk; | |
struct clk *ahb_clk; | |
struct clk *axi_clk; | |
+ struct clk *mmss_misc_ahb_clk; | |
struct clk *byte_clk; | |
struct clk *esc_clk; | |
struct clk *pixel_clk; | |
u8 ctrl_state; | |
int panel_mode; | |
int irq_cnt; | |
- int mdss_dsi_clk_on; | |
int rst_gpio; | |
int disp_en_gpio; | |
int disp_te_gpio; | |
int mode_gpio; | |
- int rst_gpio_requested; | |
- int disp_en_gpio_requested; | |
int disp_te_gpio_requested; | |
- int mode_gpio_requested; | |
int bklt_ctrl; /* backlight ctrl */ | |
int pwm_period; | |
int pwm_pmic_gpio; | |
@@ -263,6 +281,7 @@ struct mdss_dsi_ctrl_pdata { | |
int bklt_max; | |
int new_fps; | |
int pwm_enabled; | |
+ bool dmap_iommu_map; | |
struct pwm_device *pwm_bl; | |
struct dsi_drv_cm_data shared_pdata; | |
u32 pclk_rate; | |
@@ -274,6 +293,11 @@ struct mdss_dsi_ctrl_pdata { | |
struct dsi_panel_cmds on_cmds; | |
struct dsi_panel_cmds off_cmds; | |
+ struct dsi_panel_cmds status_cmds; | |
+ u32 status_value; | |
+ | |
+ struct dsi_panel_cmds video2cmd; | |
+ struct dsi_panel_cmds cmd2video; | |
struct dcs_cmd_list cmdlist; | |
struct completion dma_comp; | |
@@ -286,14 +310,18 @@ struct mdss_dsi_ctrl_pdata { | |
struct mutex mutex; | |
struct mutex cmd_mutex; | |
+ bool ulps; | |
+ | |
struct dsi_buf tx_buf; | |
struct dsi_buf rx_buf; | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- int lcd_pm_en_gpio; | |
- int bl_en_gpio; | |
- int lcd_dsv_enp_gpio; | |
- int lcd_dsv_enn_gpio; | |
-#endif | |
+ struct dsi_buf status_buf; | |
+ int status_mode; | |
+}; | |
+ | |
+struct dsi_status_data { | |
+ struct notifier_block fb_notifier; | |
+ struct delayed_work check_status; | |
+ struct msm_fb_data_type *mfd; | |
}; | |
int dsi_panel_device_register(struct device_node *pan_node, | |
@@ -305,8 +333,7 @@ int mdss_dsi_cmds_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, | |
struct dsi_cmd_desc *cmds, int rlen); | |
-void mdss_dsi_host_init(struct mipi_panel_info *pinfo, | |
- struct mdss_panel_data *pdata); | |
+void mdss_dsi_host_init(struct mdss_panel_data *pdata); | |
void mdss_dsi_op_mode_config(int mode, | |
struct mdss_panel_data *pdata); | |
void mdss_dsi_cmd_mode_ctrl(int enable); | |
@@ -314,7 +341,8 @@ void mdp4_dsi_cmd_trigger(void); | |
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl); | |
void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata); | |
void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl); | |
-int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable); | |
+int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, | |
+ u8 clk_type, int enable); | |
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, | |
int enable); | |
void mdss_dsi_controller_cfg(int enable, | |
@@ -324,7 +352,7 @@ void mdss_dsi_sw_reset(struct mdss_panel_data *pdata); | |
irqreturn_t mdss_dsi_isr(int irq, void *ptr); | |
void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata); | |
-void mipi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); | |
+void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); | |
int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, | |
int frame_rate); | |
int mdss_dsi_clk_init(struct platform_device *pdev, | |
@@ -332,8 +360,8 @@ int mdss_dsi_clk_init(struct platform_device *pdev, | |
void mdss_dsi_clk_deinit(struct mdss_dsi_ctrl_pdata *ctrl_pdata); | |
int mdss_dsi_enable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata); | |
void mdss_dsi_disable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata); | |
-void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable); | |
-void mdss_dsi_phy_enable(struct mdss_dsi_ctrl_pdata *ctrl, int on); | |
+int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable); | |
+void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl); | |
void mdss_dsi_phy_init(struct mdss_panel_data *pdata); | |
void mdss_dsi_phy_sw_reset(unsigned char *ctrl_base); | |
void mdss_dsi_cmd_test_pattern(struct mdss_dsi_ctrl_pdata *ctrl); | |
@@ -346,11 +374,58 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl); | |
int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp); | |
void mdss_dsi_cmdlist_kickoff(int intf); | |
int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl); | |
+int mdss_dsi_reg_status_check(struct mdss_dsi_ctrl_pdata *ctrl); | |
+bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type); | |
int mdss_dsi_panel_init(struct device_node *node, | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata, | |
bool cmd_cfg_cont_splash); | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
-int nt35521_panel_power(struct mdss_panel_data *pdata, int enable); | |
-#endif | |
+int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, | |
+ char *dst_format); | |
+ | |
+int mdss_dsi_register_recovery_handler(struct mdss_dsi_ctrl_pdata *ctrl, | |
+ struct mdss_panel_recovery *recovery); | |
+ | |
+static inline bool mdss_dsi_broadcast_mode_enabled(void) | |
+{ | |
+ return ctrl_list[DSI_CTRL_MASTER]->shared_pdata.broadcast_enable && | |
+ ctrl_list[DSI_CTRL_SLAVE] && | |
+ ctrl_list[DSI_CTRL_SLAVE]->shared_pdata.broadcast_enable; | |
+} | |
+ | |
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_master_ctrl(void) | |
+{ | |
+ if (mdss_dsi_broadcast_mode_enabled()) | |
+ return ctrl_list[DSI_CTRL_MASTER]; | |
+ else | |
+ return NULL; | |
+} | |
+ | |
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_slave_ctrl(void) | |
+{ | |
+ if (mdss_dsi_broadcast_mode_enabled()) | |
+ return ctrl_list[DSI_CTRL_SLAVE]; | |
+ else | |
+ return NULL; | |
+} | |
+ | |
+static inline bool mdss_dsi_is_master_ctrl(struct mdss_dsi_ctrl_pdata *ctrl) | |
+{ | |
+ return mdss_dsi_broadcast_mode_enabled() && | |
+ (ctrl->ndx == DSI_CTRL_MASTER); | |
+} | |
+ | |
+static inline bool mdss_dsi_is_slave_ctrl(struct mdss_dsi_ctrl_pdata *ctrl) | |
+{ | |
+ return mdss_dsi_broadcast_mode_enabled() && | |
+ (ctrl->ndx == DSI_CTRL_SLAVE); | |
+} | |
+ | |
+static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_ctrl_by_index(int ndx) | |
+{ | |
+ if (ndx >= DSI_CTRL_MAX) | |
+ return NULL; | |
+ | |
+ return ctrl_list[ndx]; | |
+} | |
#endif /* MDSS_DSI_H */ | |
diff --git a/drivers/video/msm/mdss/mdss_dsi_cmd.c b/drivers/video/msm/mdss/mdss_dsi_cmd.c | |
index 8fc1115c0..6d8f9e9 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi_cmd.c | |
+++ b/drivers/video/msm/mdss/mdss_dsi_cmd.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -66,6 +66,7 @@ char *mdss_dsi_buf_init(struct dsi_buf *dp) | |
off = 8 - off; | |
dp->data += off; | |
dp->len = 0; | |
+ dp->read_cnt = 0; | |
return dp->data; | |
} | |
@@ -86,6 +87,7 @@ int mdss_dsi_buf_alloc(struct dsi_buf *dp, int size) | |
dp->data = dp->start; | |
dp->len = 0; | |
+ dp->read_cnt = 0; | |
return size; | |
} | |
@@ -574,6 +576,7 @@ int mdss_dsi_short_read1_resp(struct dsi_buf *rp) | |
/* strip out dcs type */ | |
rp->data++; | |
rp->len = 1; | |
+ rp->read_cnt -= 3; | |
return rp->len; | |
} | |
@@ -585,6 +588,7 @@ int mdss_dsi_short_read2_resp(struct dsi_buf *rp) | |
/* strip out dcs type */ | |
rp->data++; | |
rp->len = 2; | |
+ rp->read_cnt -= 2; | |
return rp->len; | |
} | |
@@ -593,6 +597,7 @@ int mdss_dsi_long_read_resp(struct dsi_buf *rp) | |
/* strip out dcs header */ | |
rp->data += 4; | |
rp->len -= 4; | |
+ rp->read_cnt -= 6; | |
return rp->len; | |
} | |
diff --git a/drivers/video/msm/mdss/mdss_dsi_cmd.h b/drivers/video/msm/mdss/mdss_dsi_cmd.h | |
index f806e78..7ad2d71 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi_cmd.h | |
+++ b/drivers/video/msm/mdss/mdss_dsi_cmd.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -30,7 +30,7 @@ struct mdss_dsi_ctrl_pdata; | |
#define MDSS_DSI_MRPS 0x04 /* Maximum Return Packet Size */ | |
-#define MDSS_DSI_LEN 8 /* 4 x 4 - 6 - 2, bytes dcs header+crc-align */ | |
+#define MDSS_DSI_LEN 10 /* 4 x 4 - 4 - 2, bytes dcs header+crc-align */ | |
struct dsi_buf { | |
u32 *hdr; /* dsi host header */ | |
@@ -40,6 +40,7 @@ struct dsi_buf { | |
char *data; /* buffer */ | |
int len; /* data length */ | |
dma_addr_t dmap; /* mapped dma addr */ | |
+ int read_cnt; | |
}; | |
/* dcs read/write */ | |
@@ -99,6 +100,7 @@ struct dsi_cmd_desc { | |
#define CMD_CLK_CTRL 0x0004 | |
#define CMD_REQ_NO_MAX_PKT_SIZE 0x0008 | |
#define CMD_REQ_LP_MODE 0x0010 | |
+#define CMD_REQ_HS_MODE 0x0020 | |
struct dcs_cmd_req { | |
struct dsi_cmd_desc *cmds; | |
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c | |
index bd156fc..045de27 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi_host.c | |
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c | |
@@ -26,13 +26,11 @@ | |
#include "mdss.h" | |
#include "mdss_dsi.h" | |
#include "mdss_panel.h" | |
+#include "mdss_debug.h" | |
#define VSYNC_PERIOD 17 | |
-static struct mdss_dsi_ctrl_pdata *left_ctrl_pdata; | |
- | |
-static struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; | |
- | |
+struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX]; | |
struct mdss_hw mdss_dsi0_hw = { | |
.hw_ndx = MDSS_HW_DSI0, | |
@@ -72,14 +70,6 @@ static int dsi_event_thread(void *data); | |
void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) | |
{ | |
- if (ctrl->shared_pdata.broadcast_enable) | |
- if (ctrl->panel_data.panel_info.pdest | |
- == DISPLAY_1) { | |
- pr_debug("%s: Broadcast mode enabled.\n", | |
- __func__); | |
- left_ctrl_pdata = ctrl; | |
- } | |
- | |
if (ctrl->panel_data.panel_info.pdest == DISPLAY_1) { | |
mdss_dsi0_hw.ptr = (void *)(ctrl); | |
ctrl->dsi_hw = &mdss_dsi0_hw; | |
@@ -90,6 +80,8 @@ void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) | |
ctrl->ndx = DSI_CTRL_1; | |
} | |
+ ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode; | |
+ | |
ctrl_list[ctrl->ndx] = ctrl; /* keep it */ | |
if (mdss_register_irq(ctrl->dsi_hw)) | |
@@ -107,6 +99,7 @@ void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) | |
mutex_init(&ctrl->cmd_mutex); | |
mdss_dsi_buf_alloc(&ctrl->tx_buf, SZ_4K); | |
mdss_dsi_buf_alloc(&ctrl->rx_buf, SZ_4K); | |
+ mdss_dsi_buf_alloc(&ctrl->status_buf, SZ_4K); | |
ctrl->cmdlist_commit = mdss_dsi_cmdlist_commit; | |
@@ -119,6 +112,7 @@ void mdss_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) | |
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable) | |
{ | |
+ MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid); | |
if (enable == 0) { | |
/* need wait before disable */ | |
mutex_lock(&ctrl->cmd_mutex); | |
@@ -126,22 +120,27 @@ void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl, int enable) | |
mutex_unlock(&ctrl->cmd_mutex); | |
} | |
- mdss_dsi_clk_ctrl(ctrl, enable); | |
+ MDSS_XLOG(ctrl->ndx, enable, ctrl->mdp_busy, current->pid); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, enable); | |
} | |
void mdss_dsi_pll_relock(struct mdss_dsi_ctrl_pdata *ctrl) | |
{ | |
int i, cnt; | |
- cnt = ctrl->clk_cnt; | |
+ /* | |
+ * todo: this code does not work very well with dual | |
+ * dsi use cases. Need to fix this eventually. | |
+ */ | |
+ cnt = ctrl->link_clk_cnt; | |
/* disable dsi clk */ | |
for (i = 0; i < cnt; i++) | |
- mdss_dsi_clk_ctrl(ctrl, 0); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_LINK_CLKS, 0); | |
/* enable dsi clk */ | |
for (i = 0; i < cnt; i++) | |
- mdss_dsi_clk_ctrl(ctrl, 1); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_LINK_CLKS, 1); | |
} | |
void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) | |
@@ -154,6 +153,7 @@ void mdss_dsi_enable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) | |
return; | |
} | |
if (ctrl->dsi_irq_mask == 0) { | |
+ MDSS_XLOG(ctrl->ndx, term); | |
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); | |
@@ -173,6 +173,7 @@ void mdss_dsi_disable_irq(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) | |
} | |
ctrl->dsi_irq_mask &= ~term; | |
if (ctrl->dsi_irq_mask == 0) { | |
+ MDSS_XLOG(ctrl->ndx, term); | |
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); | |
@@ -193,6 +194,7 @@ void mdss_dsi_disable_irq_nosync(struct mdss_dsi_ctrl_pdata *ctrl, u32 term) | |
} | |
ctrl->dsi_irq_mask &= ~term; | |
if (ctrl->dsi_irq_mask == 0) { | |
+ MDSS_XLOG(ctrl->ndx, term); | |
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); | |
@@ -230,12 +232,12 @@ void mdss_dsi_cmd_test_pattern(struct mdss_dsi_ctrl_pdata *ctrl) | |
MIPI_OUTP((ctrl->ctrl_base) + 0x015c, 0x0); | |
} | |
-void mdss_dsi_host_init(struct mipi_panel_info *pinfo, | |
- struct mdss_panel_data *pdata) | |
+void mdss_dsi_host_init(struct mdss_panel_data *pdata) | |
{ | |
u32 dsi_ctrl, intr_ctrl; | |
u32 data; | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ struct mipi_panel_info *pinfo = NULL; | |
if (pdata == NULL) { | |
pr_err("%s: Invalid input data\n", __func__); | |
@@ -245,12 +247,14 @@ void mdss_dsi_host_init(struct mipi_panel_info *pinfo, | |
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
panel_data); | |
- pinfo->rgb_swap = DSI_RGB_SWAP_RGB; | |
+ pinfo = &pdata->panel_info.mipi; | |
- ctrl_pdata->panel_mode = pinfo->mode; | |
+ pinfo->rgb_swap = DSI_RGB_SWAP_RGB; | |
if (pinfo->mode == DSI_VIDEO_MODE) { | |
data = 0; | |
+ if (pinfo->last_line_interleave_en) | |
+ data |= BIT(31); | |
if (pinfo->pulse_mode_hsa_he) | |
data |= BIT(28); | |
if (pinfo->hfp_power_stop) | |
@@ -319,7 +323,7 @@ void mdss_dsi_host_init(struct mipi_panel_info *pinfo, | |
/* from frame buffer, low power mode */ | |
/* DSI_COMMAND_MODE_DMA_CTRL */ | |
- if (ctrl_pdata->shared_pdata.broadcast_enable) | |
+ if (mdss_dsi_broadcast_mode_enabled()) | |
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x94000000); | |
else | |
MIPI_OUTP(ctrl_pdata->ctrl_base + 0x3C, 0x14000000); | |
@@ -370,7 +374,7 @@ void mdss_dsi_host_init(struct mipi_panel_info *pinfo, | |
wmb(); | |
} | |
-void mdss_set_tx_power_mode(int mode, struct mdss_panel_data *pdata) | |
+void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata) | |
{ | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
u32 data; | |
@@ -522,6 +526,7 @@ void mdss_dsi_op_mode_config(int mode, | |
{ | |
u32 dsi_ctrl, intr_ctrl; | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
if (pdata == NULL) { | |
pr_err("%s: Invalid input data\n", __func__); | |
@@ -531,12 +536,15 @@ void mdss_dsi_op_mode_config(int mode, | |
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; | |
- } | |
+ /* | |
+ * In broadcast mode, the configuration for master controller | |
+ * would be done when the slave controller is configured | |
+ */ | |
+ if (mdss_dsi_is_master_ctrl(ctrl_pdata)) { | |
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n", | |
+ __func__, ctrl_pdata->ndx); | |
+ return; | |
+ } | |
dsi_ctrl = MIPI_INP((ctrl_pdata->ctrl_base) + 0x0004); | |
/*If Video enabled, Keep Video and Cmd mode ON */ | |
@@ -557,17 +565,22 @@ void mdss_dsi_op_mode_config(int mode, | |
DSI_INTR_CMD_MDP_DONE_MASK | DSI_INTR_BTA_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); | |
+ /* Ensure that for slave controller, master is also configured */ | |
+ if (mdss_dsi_is_slave_ctrl(ctrl_pdata)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (mctrl) { | |
+ pr_debug("%s: configuring ctrl%d\n", __func__, | |
+ mctrl->ndx); | |
+ MIPI_OUTP(mctrl->ctrl_base + 0x0110, intr_ctrl); | |
+ MIPI_OUTP(mctrl->ctrl_base + 0x0004, dsi_ctrl); | |
+ } else { | |
+ pr_warn("%s: Unable to get master control\n", | |
+ __func__); | |
} | |
+ } | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, | |
- intr_ctrl); /* DSI_INTL_CTRL */ | |
+ pr_debug("%s: configuring ctrl%d\n", __func__, ctrl_pdata->ndx); | |
+ MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0110, intr_ctrl); | |
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0004, dsi_ctrl); | |
wmb(); | |
} | |
@@ -600,6 +613,82 @@ void mdss_dsi_cmd_bta_sw_trigger(struct mdss_panel_data *pdata) | |
pr_debug("%s: BTA done, status = %d\n", __func__, status); | |
} | |
+static int mdss_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 = 0; | |
+ cmdreq.cb = NULL; | |
+ cmdreq.rbuf = ctrl->status_buf.data; | |
+ | |
+ return mdss_dsi_cmdlist_put(ctrl, &cmdreq); | |
+} | |
+ | |
+ | |
+/** | |
+ * mdss_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 mdss_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__); | |
+ | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
+ | |
+ if (ctrl_pdata->status_cmds.link_state == DSI_HS_MODE) | |
+ mdss_dsi_set_tx_power_mode(0, &ctrl_pdata->panel_data); | |
+ | |
+ ret = mdss_dsi_read_status(ctrl_pdata); | |
+ | |
+ if (ctrl_pdata->status_cmds.link_state == DSI_HS_MODE) | |
+ mdss_dsi_set_tx_power_mode(1, &ctrl_pdata->panel_data); | |
+ | |
+ if (ret == 0) { | |
+ if (ctrl_pdata->status_buf.data[0] != | |
+ ctrl_pdata->status_value) { | |
+ 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__); | |
+ } | |
+ | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
+ pr_debug("%s: Read register done with ret: %d\n", __func__, ret); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/** | |
+ * mdss_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. | |
+ */ | |
int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
int ret = 0; | |
@@ -618,7 +707,7 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
pr_debug("%s: Checking BTA status\n", __func__); | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 1); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); | |
spin_lock_irqsave(&ctrl_pdata->mdp_lock, flag); | |
INIT_COMPLETION(ctrl_pdata->bta_comp); | |
mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM); | |
@@ -633,7 +722,7 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
pr_err("%s: DSI BTA error: %i\n", __func__, ret); | |
} | |
- mdss_dsi_clk_ctrl(ctrl_pdata, 0); | |
+ mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0); | |
pr_debug("%s: BTA done with ret: %d\n", __func__, ret); | |
return ret; | |
@@ -722,6 +811,40 @@ static int mdss_dsi_cmds2buf_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
return tot; | |
} | |
+/** | |
+ * __mdss_dsi_cmd_mode_config() - Enable/disable command mode engine | |
+ * @ctrl: pointer to the dsi controller structure | |
+ * @enable: true to enable command mode, false to disable command mode | |
+ * | |
+ * This function can be used to temporarily enable the command mode | |
+ * engine (even for video mode panels) so as to transfer any dma commands to | |
+ * the panel. It can also be used to disable the command mode engine | |
+ * when no longer needed. | |
+ * | |
+ * Return: true, if there was a mode switch to command mode for video mode | |
+ * panels. | |
+ */ | |
+static inline bool __mdss_dsi_cmd_mode_config( | |
+ struct mdss_dsi_ctrl_pdata *ctrl, bool enable) | |
+{ | |
+ bool mode_changed = false; | |
+ u32 dsi_ctrl; | |
+ | |
+ dsi_ctrl = MIPI_INP((ctrl->ctrl_base) + 0x0004); | |
+ /* if currently in video mode, enable command mode */ | |
+ if (enable) { | |
+ if ((dsi_ctrl) & BIT(1)) { | |
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, | |
+ dsi_ctrl | BIT(2)); | |
+ mode_changed = true; | |
+ } | |
+ } else { | |
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0004, dsi_ctrl & ~BIT(2)); | |
+ } | |
+ | |
+ return mode_changed; | |
+} | |
+ | |
/* | |
* mdss_dsi_cmds_tx: | |
* thread context only | |
@@ -729,61 +852,49 @@ static int mdss_dsi_cmds2buf_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
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, ret = 0; | |
- u32 left_dsi_ctrl = 0; | |
- bool left_ctrl_restore = false; | |
- | |
- if (ctrl->shared_pdata.broadcast_enable) { | |
- if (ctrl->ndx == DSI_CTRL_0) { | |
- pr_debug("%s: Broadcast mode. 1st ctrl\n", | |
- __func__); | |
- return 0; | |
- } | |
- } | |
+ int ret = 0; | |
+ bool ctrl_restore = false, mctrl_restore = false; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
- if (ctrl->shared_pdata.broadcast_enable) { | |
- if ((ctrl->ndx == DSI_CTRL_1) | |
- && (left_ctrl_pdata != NULL)) { | |
- left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base | |
- + 0x0004); | |
- video_mode = | |
- left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */ | |
- if (video_mode) { | |
- data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */ | |
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004, | |
- data); | |
- left_ctrl_restore = true; | |
- } | |
- } | |
+ /* | |
+ * In broadcast mode, the configuration for master controller | |
+ * would be done when the slave controller is configured | |
+ */ | |
+ if (mdss_dsi_is_master_ctrl(ctrl)) { | |
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n", | |
+ __func__, ctrl->ndx); | |
+ return 0; | |
} | |
- /* 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); | |
+ /* | |
+ * 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. | |
+ * Ensure that for slave controller, master is also configured | |
+ */ | |
+ if (mdss_dsi_is_slave_ctrl(ctrl)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (!mctrl) | |
+ pr_warn("%s: Unable to get master control\n", | |
+ __func__); | |
+ else | |
+ mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1); | |
} | |
+ ctrl_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__); | |
+ pr_err("%s: failed to call\n", __func__); | |
cnt = -EINVAL; | |
} | |
- if (left_ctrl_restore) | |
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004, | |
- left_dsi_ctrl); /*restore */ | |
+ if (mctrl_restore) | |
+ __mdss_dsi_cmd_mode_config(mctrl, 0); | |
+ | |
+ if (ctrl_restore) | |
+ __mdss_dsi_cmd_mode_config(ctrl, 0); | |
- if (video_mode) | |
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004, | |
- dsi_ctrl); /* restore */ | |
return cnt; | |
} | |
@@ -816,48 +927,39 @@ int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, | |
int short_response, diff, pkt_size, ret = 0; | |
struct dsi_buf *tp, *rp; | |
char cmd; | |
- u32 dsi_ctrl, data; | |
- int video_mode; | |
- u32 left_dsi_ctrl = 0; | |
- bool left_ctrl_restore = false; | |
- | |
- if (ctrl->shared_pdata.broadcast_enable) { | |
- if (ctrl->ndx == DSI_CTRL_0) { | |
- pr_debug("%s: Broadcast mode. 1st ctrl\n", | |
- __func__); | |
- return 0; | |
- } | |
- } | |
+ bool ctrl_restore = false, mctrl_restore = false; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
- if (ctrl->shared_pdata.broadcast_enable) { | |
- if ((ctrl->ndx == DSI_CTRL_1) | |
- && (left_ctrl_pdata != NULL)) { | |
- left_dsi_ctrl = MIPI_INP(left_ctrl_pdata->ctrl_base | |
- + 0x0004); | |
- video_mode = left_dsi_ctrl & 0x02; /* VIDEO_MODE_EN */ | |
- if (video_mode) { | |
- data = left_dsi_ctrl | 0x04; /* CMD_MODE_EN */ | |
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004, | |
- data); | |
- left_ctrl_restore = true; | |
- } | |
- } | |
+ /* | |
+ * In broadcast mode, the configuration for master controller | |
+ * would be done when the slave controller is configured | |
+ */ | |
+ if (mdss_dsi_is_master_ctrl(ctrl)) { | |
+ pr_debug("%s: Broadcast mode enabled. skipping config for ctrl%d\n", | |
+ __func__, ctrl->ndx); | |
+ return 0; | |
} | |
- /* 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); | |
+ /* | |
+ * 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. | |
+ * Ensure that for slave controller, master is also configured | |
+ */ | |
+ if (mdss_dsi_is_slave_ctrl(ctrl)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (!mctrl) | |
+ pr_warn("%s: Unable to get master control\n", | |
+ __func__); | |
+ else | |
+ mctrl_restore = __mdss_dsi_cmd_mode_config(mctrl, 1); | |
} | |
- if (rlen == 0) { | |
+ ctrl_restore = __mdss_dsi_cmd_mode_config(ctrl, 1); | |
+ | |
+ if (rlen <= 2) { | |
short_response = 1; | |
+ pkt_size = rlen; | |
rx_byte = 4; | |
} else { | |
short_response = 0; | |
@@ -881,31 +983,29 @@ int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, | |
while (!end) { | |
pr_debug("%s: rlen=%d pkt_size=%d rx_byte=%d\n", | |
__func__, rlen, pkt_size, rx_byte); | |
- if (!short_response) { | |
- 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; | |
- } | |
+ 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_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_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); | |
@@ -981,12 +1081,11 @@ int mdss_dsi_cmds_rx(struct mdss_dsi_ctrl_pdata *ctrl, | |
rp->len = 0; | |
} | |
end: | |
- if (left_ctrl_restore) | |
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0004, | |
- left_dsi_ctrl); /*restore */ | |
- if (video_mode) | |
- MIPI_OUTP((ctrl->ctrl_base) + 0x0004, | |
- dsi_ctrl); /* restore */ | |
+ if (mctrl_restore) | |
+ __mdss_dsi_cmd_mode_config(mctrl, 0); | |
+ | |
+ if (ctrl_restore) | |
+ __mdss_dsi_cmd_mode_config(ctrl, 0); | |
return rp->len; | |
} | |
@@ -1000,6 +1099,7 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
int domain = MDSS_IOMMU_DOMAIN_UNSECURE; | |
char *bp; | |
unsigned long size, addr; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
bp = tp->data; | |
@@ -1008,37 +1108,41 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
if (is_mdss_iommu_attached()) { | |
- int ret = msm_iommu_map_contig_buffer(tp->dmap, | |
+ 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; | |
} | |
+ ctrl->dmap_iommu_map = true; | |
} 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); | |
+ /* Ensure that for slave controller, master is also configured */ | |
+ if (mdss_dsi_is_slave_ctrl(ctrl)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (mctrl) { | |
+ MIPI_OUTP(mctrl->ctrl_base + 0x048, addr); | |
+ MIPI_OUTP(mctrl->ctrl_base + 0x04c, len); | |
+ } else { | |
+ pr_warn("%s: Unable to get master control\n", | |
+ __func__); | |
} | |
+ } | |
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); | |
- } | |
+ /* Trigger on master controller as well */ | |
+ if (mctrl) | |
+ MIPI_OUTP(mctrl->ctrl_base + 0x090, 0x01); | |
- MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); /* trigger */ | |
+ MIPI_OUTP((ctrl->ctrl_base) + 0x090, 0x01); | |
wmb(); | |
ret = wait_for_completion_timeout(&ctrl->dma_comp, | |
@@ -1048,9 +1152,11 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
else | |
ret = tp->len; | |
- if (is_mdss_iommu_attached()) | |
+ if (ctrl->dmap_iommu_map) { | |
msm_iommu_unmap_contig_buffer(addr, | |
mdss_get_iommu_domain(domain), 0, size); | |
+ ctrl->dmap_iommu_map = false; | |
+ } | |
return ret; | |
} | |
@@ -1134,6 +1240,7 @@ void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl) | |
mdss_dsi_enable_irq(ctrl, DSI_MDP_TERM); | |
ctrl->mdp_busy = true; | |
INIT_COMPLETION(ctrl->mdp_comp); | |
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid); | |
spin_unlock_irqrestore(&ctrl->mdp_lock, flag); | |
} | |
@@ -1144,6 +1251,8 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) | |
pr_debug("%s: start pid=%d\n", | |
__func__, current->pid); | |
+ | |
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid, XLOG_FUNC_ENTRY); | |
spin_lock_irqsave(&ctrl->mdp_lock, flags); | |
if (ctrl->mdp_busy == true) | |
need_wait++; | |
@@ -1154,11 +1263,14 @@ void mdss_dsi_cmd_mdp_busy(struct mdss_dsi_ctrl_pdata *ctrl) | |
pr_debug("%s: pending pid=%d\n", | |
__func__, current->pid); | |
if (!wait_for_completion_timeout(&ctrl->mdp_comp, | |
- msecs_to_jiffies(DMA_TX_TIMEOUT))) | |
+ msecs_to_jiffies(DMA_TX_TIMEOUT))) { | |
pr_err("%s: timeout error\n", __func__); | |
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", | |
+ "edp", "hdmi", "panic"); | |
+ } | |
} | |
- pr_debug("%s: done pid=%d\n", | |
- __func__, current->pid); | |
+ pr_debug("%s: done pid=%d\n", __func__, current->pid); | |
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, current->pid, XLOG_FUNC_EXIT); | |
} | |
int mdss_dsi_cmdlist_tx(struct mdss_dsi_ctrl_pdata *ctrl, | |
@@ -1188,13 +1300,11 @@ int mdss_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl, | |
len = mdss_dsi_cmds_rx(ctrl, req->cmds, req->rlen); | |
memcpy(req->rbuf, rp->data, rp->len); | |
/* | |
- * For dual DSI cases, early return of controller - 0 | |
+ * For dual DSI cases, early return of master ctrl | |
* is valid. Hence, for those cases the return value | |
* is zero even though we don't send any commands. | |
- * | |
*/ | |
- if ((ctrl->shared_pdata.broadcast_enable && | |
- ctrl->ndx == DSI_CTRL_0) || (len != 0)) | |
+ if (mdss_dsi_is_master_ctrl(ctrl) || (len != 0)) | |
ret = 0; | |
} else { | |
pr_err("%s: No rx buffer provided\n", __func__); | |
@@ -1210,10 +1320,13 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) | |
{ | |
struct dcs_cmd_req *req; | |
int ret = -EINVAL; | |
- | |
+ int rc = 0; | |
mutex_lock(&ctrl->cmd_mutex); | |
req = mdss_dsi_cmdlist_get(ctrl); | |
+ MDSS_XLOG(ctrl->ndx, from_mdp, ctrl->mdp_busy, current->pid, | |
+ XLOG_FUNC_ENTRY); | |
+ | |
/* make sure dsi_cmd_mdp is idle */ | |
mdss_dsi_cmd_mdp_busy(ctrl); | |
@@ -1222,34 +1335,85 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp) | |
if (req == NULL) | |
goto need_lock; | |
+ MDSS_XLOG(ctrl->ndx, req->flags, req->cmds_cnt, from_mdp, current->pid); | |
+ | |
/* | |
* 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 | |
*/ | |
- mdss_bus_bandwidth_ctrl(1); | |
+ mdss_bus_scale_set_quota(MDSS_HW_DSI0, SZ_1M, SZ_1M); | |
pr_debug("%s: from_mdp=%d pid=%d\n", __func__, from_mdp, current->pid); | |
- mdss_dsi_clk_ctrl(ctrl, 1); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 1); | |
+ | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ mutex_unlock(&ctrl->cmd_mutex); | |
+ return rc; | |
+ } | |
+ | |
+ if (req->flags & CMD_REQ_HS_MODE) | |
+ mdss_dsi_set_tx_power_mode(0, &ctrl->panel_data); | |
if (req->flags & CMD_REQ_RX) | |
ret = mdss_dsi_cmdlist_rx(ctrl, req); | |
else | |
ret = mdss_dsi_cmdlist_tx(ctrl, req); | |
- mdss_dsi_clk_ctrl(ctrl, 0); | |
- mdss_bus_bandwidth_ctrl(0); | |
+ if (req->flags & CMD_REQ_HS_MODE) | |
+ mdss_dsi_set_tx_power_mode(1, &ctrl->panel_data); | |
+ mdss_iommu_ctrl(0); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 0); | |
+ mdss_bus_scale_set_quota(MDSS_HW_DSI0, 0, 0); | |
need_lock: | |
if (from_mdp) /* from pipe_commit */ | |
mdss_dsi_cmd_mdp_start(ctrl); | |
+ MDSS_XLOG(ctrl->ndx, from_mdp, ctrl->mdp_busy, current->pid, | |
+ XLOG_FUNC_EXIT); | |
mutex_unlock(&ctrl->cmd_mutex); | |
return ret; | |
} | |
+void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata) | |
+{ | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ u8 rc, te_count = 0; | |
+ u8 te_max = 250; | |
+ | |
+ if (pdata == NULL) { | |
+ pr_err("%s: Invalid input data\n", __func__); | |
+ return; | |
+ } | |
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
+ panel_data); | |
+ | |
+ pr_info(" ============ start waiting for TE ============\n"); | |
+ for (te_count = 0; te_count < te_max; te_count++) { | |
+ rc = gpio_get_value(ctrl_pdata->disp_te_gpio); | |
+ if (rc != 0) { | |
+ pr_info("%s: gpio_get_value(disp_te_gpio) = %d ", | |
+ __func__, rc); | |
+ pr_info("te_count = %d\n", te_count); | |
+ break; | |
+ } | |
+ /* usleep suspends the calling thread whereas udelay is a | |
+ * busy wait. Here the value of te_gpio is checked in a loop of | |
+ * max count = 250. If this loop has to iterate multiple | |
+ * times before the te_gpio is 1, the calling thread will end | |
+ * up in suspend/wakeup sequence multiple times if usleep is | |
+ * used, which is an overhead. So use udelay instead of usleep. | |
+ */ | |
+ udelay(80); | |
+ } | |
+ pr_info(" ============ finish waiting for TE ============\n"); | |
+} | |
+ | |
static void dsi_send_events(struct mdss_dsi_ctrl_pdata *ctrl, u32 events) | |
{ | |
struct dsi_event_q *evq; | |
@@ -1304,21 +1468,30 @@ static int dsi_event_thread(void *data) | |
mdss_dsi_pll_relock(ctrl); | |
if (todo & DSI_EV_MDP_FIFO_UNDERFLOW) { | |
+ mutex_lock(&ctrl->mutex); | |
if (ctrl->recovery) { | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 1); | |
mdss_dsi_sw_reset_restore(ctrl); | |
ctrl->recovery->fxn(ctrl->recovery->data); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 0); | |
} | |
+ mutex_unlock(&ctrl->mutex); | |
} | |
+ if (todo & DSI_EV_DSI_FIFO_EMPTY) | |
+ mdss_dsi_sw_reset_restore(ctrl); | |
+ | |
if (todo & DSI_EV_MDP_BUSY_RELEASE) { | |
- spin_lock(&ctrl->mdp_lock); | |
+ spin_lock_irqsave(&ctrl->mdp_lock, flag); | |
ctrl->mdp_busy = false; | |
mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM); | |
complete(&ctrl->mdp_comp); | |
- spin_unlock(&ctrl->mdp_lock); | |
+ spin_unlock_irqrestore(&ctrl->mdp_lock, flag); | |
/* enable dsi error interrupt */ | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 1); | |
mdss_dsi_err_intr_ctrl(ctrl, DSI_INTR_ERROR_MASK, 1); | |
+ mdss_dsi_clk_ctrl(ctrl, DSI_ALL_CLKS, 0); | |
} | |
} | |
@@ -1382,12 +1555,16 @@ void mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl) | |
status = MIPI_INP(base + 0x000c);/* DSI_FIFO_STATUS */ | |
- /* fifo underflow, overflow */ | |
+ /* fifo underflow, overflow and empty*/ | |
if (status & 0xcccc4489) { | |
MIPI_OUTP(base + 0x000c, status); | |
pr_err("%s: status=%x\n", __func__, status); | |
if (status & 0x0080) /* CMD_DMA_FIFO_UNDERFLOW */ | |
dsi_send_events(ctrl, DSI_EV_MDP_FIFO_UNDERFLOW); | |
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", | |
+ "edp", "hdmi", "panic"); | |
+ if (status & 0x11110000) /* DLN_FIFO_EMPTY */ | |
+ dsi_send_events(ctrl, DSI_EV_DSI_FIFO_EMPTY); | |
} | |
} | |
@@ -1443,27 +1620,36 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr) | |
u32 isr; | |
struct mdss_dsi_ctrl_pdata *ctrl = | |
(struct mdss_dsi_ctrl_pdata *)ptr; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
- if (!ctrl->ctrl_base) | |
+ if (!ctrl->ctrl_base) { | |
pr_err("%s:%d DSI base adr no Initialized", | |
- __func__, __LINE__); | |
+ __func__, __LINE__); | |
+ return IRQ_HANDLED; | |
+ } | |
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)) { | |
+ if (mdss_dsi_is_slave_ctrl(ctrl)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (mctrl) { | |
u32 isr0; | |
- isr0 = MIPI_INP(left_ctrl_pdata->ctrl_base | |
- + 0x0110);/* DSI_INTR_CTRL */ | |
- MIPI_OUTP(left_ctrl_pdata->ctrl_base + 0x0110, isr0); | |
+ isr0 = MIPI_INP(mctrl->ctrl_base + 0x0110); | |
+ if (isr0 & DSI_INTR_CMD_DMA_DONE) | |
+ MIPI_OUTP(mctrl->ctrl_base + 0x0110, | |
+ DSI_INTR_CMD_DMA_DONE); | |
+ } else { | |
+ pr_warn("%s: Unable to get master control\n", | |
+ __func__); | |
} | |
+ } | |
- pr_debug("%s: isr=%x", __func__, isr); | |
+ pr_debug("%s: ndx=%d isr=%x\n", __func__, ctrl->ndx, isr); | |
if (isr & DSI_INTR_ERROR) { | |
- pr_err("%s: isr=%x %x", __func__, isr, (int)DSI_INTR_ERROR); | |
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x97); | |
+ pr_err("%s: ndx=%d isr=%x\n", __func__, ctrl->ndx, isr); | |
mdss_dsi_error(ctrl); | |
} | |
@@ -1475,6 +1661,7 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr) | |
} | |
if (isr & DSI_INTR_CMD_DMA_DONE) { | |
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x98); | |
spin_lock(&ctrl->mdp_lock); | |
mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM); | |
complete(&ctrl->dma_comp); | |
@@ -1482,6 +1669,7 @@ irqreturn_t mdss_dsi_isr(int irq, void *ptr) | |
} | |
if (isr & DSI_INTR_CMD_MDP_DONE) { | |
+ MDSS_XLOG(ctrl->ndx, ctrl->mdp_busy, isr, 0x99); | |
spin_lock(&ctrl->mdp_lock); | |
ctrl->mdp_busy = false; | |
mdss_dsi_disable_irq_nosync(ctrl, DSI_MDP_TERM); | |
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c | |
index b49046b..6e2a7f5 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c | |
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -28,10 +28,6 @@ | |
DEFINE_LED_TRIGGER(bl_led_trigger); | |
-#if defined(CONFIG_BACKLIGHT_LM3630) | |
-extern void lm3630_lcd_backlight_set_level(int level); | |
-#endif | |
- | |
void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl) | |
{ | |
ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt"); | |
@@ -45,6 +41,7 @@ static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) | |
{ | |
int ret; | |
u32 duty; | |
+ u32 period_ns; | |
if (ctrl->pwm_bl == NULL) { | |
pr_err("%s: no PWM\n", __func__); | |
@@ -73,10 +70,23 @@ static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) | |
ctrl->pwm_enabled = 0; | |
} | |
- ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); | |
- if (ret) { | |
- pr_err("%s: pwm_config_us() failed err=%d.\n", __func__, ret); | |
- return; | |
+ if (ctrl->pwm_period >= USEC_PER_SEC) { | |
+ ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); | |
+ if (ret) { | |
+ pr_err("%s: pwm_config_us() failed err=%d.\n", | |
+ __func__, ret); | |
+ return; | |
+ } | |
+ } else { | |
+ period_ns = ctrl->pwm_period * NSEC_PER_USEC; | |
+ ret = pwm_config(ctrl->pwm_bl, | |
+ level * period_ns / ctrl->bklt_max, | |
+ period_ns); | |
+ if (ret) { | |
+ pr_err("%s: pwm_config() failed err=%d.\n", | |
+ __func__, ret); | |
+ return; | |
+ } | |
} | |
ret = pwm_enable(ctrl->pwm_bl); | |
@@ -157,15 +167,53 @@ static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) | |
mdss_dsi_cmdlist_put(ctrl, &cmdreq); | |
} | |
-void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) | |
+static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+{ | |
+ int rc = 0; | |
+ | |
+ if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { | |
+ rc = gpio_request(ctrl_pdata->disp_en_gpio, | |
+ "disp_enable"); | |
+ if (rc) { | |
+ pr_err("request disp_en gpio failed, rc=%d\n", | |
+ rc); | |
+ goto disp_en_gpio_err; | |
+ } | |
+ } | |
+ rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); | |
+ if (rc) { | |
+ pr_err("request reset gpio failed, rc=%d\n", | |
+ rc); | |
+ goto rst_gpio_err; | |
+ } | |
+ if (gpio_is_valid(ctrl_pdata->mode_gpio)) { | |
+ rc = gpio_request(ctrl_pdata->mode_gpio, "panel_mode"); | |
+ if (rc) { | |
+ pr_err("request panel mode gpio failed,rc=%d\n", | |
+ rc); | |
+ goto mode_gpio_err; | |
+ } | |
+ } | |
+ return rc; | |
+ | |
+mode_gpio_err: | |
+ gpio_free(ctrl_pdata->rst_gpio); | |
+rst_gpio_err: | |
+ if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
+ gpio_free(ctrl_pdata->disp_en_gpio); | |
+disp_en_gpio_err: | |
+ return rc; | |
+} | |
+ | |
+int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) | |
{ | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
struct mdss_panel_info *pinfo = NULL; | |
- int i; | |
+ int i, rc = 0; | |
if (pdata == NULL) { | |
pr_err("%s: Invalid input data\n", __func__); | |
- return; | |
+ return -EINVAL; | |
} | |
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
@@ -179,23 +227,28 @@ void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) | |
if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { | |
pr_debug("%s:%d, reset line not configured\n", | |
__func__, __LINE__); | |
- return; | |
+ return rc; | |
} | |
pr_debug("%s: enable = %d\n", __func__, enable); | |
pinfo = &(ctrl_pdata->panel_data.panel_info); | |
if (enable) { | |
-#ifndef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
- gpio_set_value((ctrl_pdata->disp_en_gpio), 1); | |
-#endif | |
- | |
- for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { | |
- gpio_set_value((ctrl_pdata->rst_gpio), | |
- pdata->panel_info.rst_seq[i]); | |
- if (pdata->panel_info.rst_seq[++i]) | |
- usleep(pdata->panel_info.rst_seq[i] * 1000); | |
+ rc = mdss_dsi_request_gpios(ctrl_pdata); | |
+ if (rc) { | |
+ pr_err("gpio request failed\n"); | |
+ return rc; | |
+ } | |
+ if (!pinfo->panel_power_on) { | |
+ if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
+ gpio_set_value((ctrl_pdata->disp_en_gpio), 1); | |
+ | |
+ for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { | |
+ gpio_set_value((ctrl_pdata->rst_gpio), | |
+ pdata->panel_info.rst_seq[i]); | |
+ if (pdata->panel_info.rst_seq[++i]) | |
+ usleep(pinfo->rst_seq[i] * 1000); | |
+ } | |
} | |
if (gpio_is_valid(ctrl_pdata->mode_gpio)) { | |
@@ -211,42 +264,17 @@ void mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) | |
pr_debug("%s: Reset panel done\n", __func__); | |
} | |
} else { | |
- gpio_set_value((ctrl_pdata->rst_gpio), 0); | |
-#ifndef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) | |
+ if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { | |
gpio_set_value((ctrl_pdata->disp_en_gpio), 0); | |
-#endif | |
- } | |
-} | |
- | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
-int nt35521_panel_power(struct mdss_panel_data *pdata, int enable) | |
-{ | |
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
- | |
- if (pdata == NULL) { | |
- pr_err("%s: Invalid input data\n", __func__); | |
- return -EINVAL; | |
- } | |
- | |
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); | |
- | |
- if (enable) { | |
- pr_info("%s: LGE NT35521 Panel Power On!\n", __func__); | |
- gpio_set_value((ctrl_pdata->lcd_dsv_enp_gpio), 1); //DSV ENP | |
- mdelay(1); | |
- gpio_set_value(ctrl_pdata->lcd_dsv_enn_gpio, 1); //DSV ENN | |
- mdelay(50); | |
- } else { | |
- pr_info("%s: LGE NT35521 Panel Power Off!\n", __func__); | |
- gpio_set_value(ctrl_pdata->lcd_dsv_enn_gpio, 0); | |
- mdelay(1); | |
- gpio_set_value((ctrl_pdata->lcd_dsv_enp_gpio), 0); | |
+ gpio_free(ctrl_pdata->disp_en_gpio); | |
+ } | |
+ gpio_set_value((ctrl_pdata->rst_gpio), 0); | |
+ gpio_free(ctrl_pdata->rst_gpio); | |
+ if (gpio_is_valid(ctrl_pdata->mode_gpio)) | |
+ gpio_free(ctrl_pdata->mode_gpio); | |
} | |
- | |
- return 0; | |
+ return rc; | |
} | |
-#endif | |
static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */ | |
static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */ | |
@@ -303,6 +331,36 @@ static int mdss_dsi_panel_partial_update(struct mdss_panel_data *pdata) | |
return rc; | |
} | |
+static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, | |
+ int mode) | |
+{ | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ struct mipi_panel_info *mipi; | |
+ struct dsi_panel_cmds *pcmds; | |
+ | |
+ if (pdata == NULL) { | |
+ pr_err("%s: Invalid input data\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ mipi = &pdata->panel_info.mipi; | |
+ | |
+ if (!mipi->dynamic_switch_enabled) | |
+ return; | |
+ | |
+ ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
+ panel_data); | |
+ | |
+ if (mode == DSI_CMD_MODE) | |
+ pcmds = &ctrl_pdata->video2cmd; | |
+ else | |
+ pcmds = &ctrl_pdata->cmd2video; | |
+ | |
+ mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds); | |
+ | |
+ return; | |
+} | |
+ | |
static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, | |
u32 bl_level) | |
{ | |
@@ -327,17 +385,23 @@ static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, | |
switch (ctrl_pdata->bklt_ctrl) { | |
case BL_WLED: | |
-#ifdef CONFIG_BACKLIGHT_LM3630 | |
- lm3630_lcd_backlight_set_level(bl_level); | |
-#else | |
led_trigger_event(bl_led_trigger, bl_level); | |
-#endif | |
break; | |
case BL_PWM: | |
mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level); | |
break; | |
case BL_DCS_CMD: | |
mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); | |
+ if (mdss_dsi_is_master_ctrl(ctrl_pdata)) { | |
+ struct mdss_dsi_ctrl_pdata *sctrl = | |
+ mdss_dsi_get_slave_ctrl(); | |
+ if (!sctrl) { | |
+ pr_err("%s: Invalid slave ctrl data\n", | |
+ __func__); | |
+ return; | |
+ } | |
+ mdss_dsi_panel_bklt_dcs(sctrl, bl_level); | |
+ } | |
break; | |
default: | |
pr_err("%s: Unknown bl_ctrl configuration\n", | |
@@ -504,11 +568,16 @@ static int mdss_dsi_parse_dcs_cmds(struct device_node *np, | |
len -= dchdr->dlen; | |
} | |
- data = of_get_property(np, link_key, NULL); | |
- if (data && !strcmp(data, "dsi_hs_mode")) | |
- pcmds->link_state = DSI_HS_MODE; | |
- else | |
- pcmds->link_state = DSI_LP_MODE; | |
+ /*Set default link state to LP Mode*/ | |
+ pcmds->link_state = DSI_LP_MODE; | |
+ | |
+ if (link_key) { | |
+ data = of_get_property(np, link_key, NULL); | |
+ if (data && !strcmp(data, "dsi_hs_mode")) | |
+ pcmds->link_state = DSI_HS_MODE; | |
+ else | |
+ pcmds->link_state = DSI_LP_MODE; | |
+ } | |
pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__, | |
pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state); | |
@@ -521,7 +590,7 @@ exit_free: | |
} | |
-static int mdss_panel_dt_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, | |
+int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, | |
char *dst_format) | |
{ | |
int rc = 0; | |
@@ -644,6 +713,41 @@ static int mdss_dsi_parse_fbc_params(struct device_node *np, | |
return 0; | |
} | |
+static void mdss_panel_parse_te_params(struct device_node *np, | |
+ struct mdss_panel_info *panel_info) | |
+{ | |
+ | |
+ u32 tmp; | |
+ int rc = 0; | |
+ /* | |
+ * TE default: dsi byte clock calculated base on 70 fps; | |
+ * around 14 ms to complete a kickoff cycle if te disabled; | |
+ * vclk_line base on 60 fps; write is faster than read; | |
+ * init == start == rdptr; | |
+ */ | |
+ panel_info->te.tear_check_en = | |
+ !of_property_read_bool(np, "qcom,mdss-tear-check-disable"); | |
+ rc = of_property_read_u32 | |
+ (np, "qcom,mdss-tear-check-sync-cfg-height", &tmp); | |
+ panel_info->te.sync_cfg_height = (!rc ? tmp : 0xfff0); | |
+ rc = of_property_read_u32 | |
+ (np, "qcom,mdss-tear-check-sync-init-val", &tmp); | |
+ panel_info->te.vsync_init_val = (!rc ? tmp : panel_info->yres); | |
+ rc = of_property_read_u32 | |
+ (np, "qcom,mdss-tear-check-sync-threshold-start", &tmp); | |
+ panel_info->te.sync_threshold_start = (!rc ? tmp : 4); | |
+ rc = of_property_read_u32 | |
+ (np, "qcom,mdss-tear-check-sync-threshold-continue", &tmp); | |
+ panel_info->te.sync_threshold_continue = (!rc ? tmp : 4); | |
+ rc = of_property_read_u32(np, "qcom,mdss-tear-check-start-pos", &tmp); | |
+ panel_info->te.start_pos = (!rc ? tmp : panel_info->yres); | |
+ rc = of_property_read_u32 | |
+ (np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp); | |
+ panel_info->te.rd_ptr_irq = (!rc ? tmp : panel_info->yres + 1); | |
+ rc = of_property_read_u32(np, "qcom,mdss-tear-check-frame-rate", &tmp); | |
+ panel_info->te.refx100 = (!rc ? tmp : 6000); | |
+} | |
+ | |
static int mdss_dsi_parse_reset_seq(struct device_node *np, | |
u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len, | |
@@ -673,6 +777,89 @@ static int mdss_dsi_parse_reset_seq(struct device_node *np, | |
return 0; | |
} | |
+static void mdss_dsi_parse_roi_alignment(struct device_node *np, | |
+ struct mdss_panel_info *pinfo) | |
+{ | |
+ int len = 0; | |
+ u32 value[6]; | |
+ struct property *data; | |
+ data = of_find_property(np, "qcom,panel-roi-alignment", &len); | |
+ len /= sizeof(u32); | |
+ if (!data || (len != 6)) { | |
+ pr_debug("%s: Panel roi alignment not found", __func__); | |
+ } else { | |
+ int rc = of_property_read_u32_array(np, | |
+ "qcom,panel-roi-alignment", value, len); | |
+ if (rc) | |
+ pr_debug("%s: Error reading panel roi alignment values", | |
+ __func__); | |
+ else { | |
+ pinfo->xstart_pix_align = value[0]; | |
+ pinfo->width_pix_align = value[1]; | |
+ pinfo->ystart_pix_align = value[2]; | |
+ pinfo->height_pix_align = value[3]; | |
+ pinfo->min_width = value[4]; | |
+ pinfo->min_height = value[5]; | |
+ } | |
+ | |
+ pr_debug("%s: ROI alignment: [%d, %d, %d, %d, %d, %d]", | |
+ __func__, pinfo->xstart_pix_align, | |
+ pinfo->width_pix_align, pinfo->ystart_pix_align, | |
+ pinfo->height_pix_align, pinfo->min_width, | |
+ pinfo->min_height); | |
+ } | |
+} | |
+ | |
+static int mdss_dsi_parse_panel_features(struct device_node *np, | |
+ struct mdss_dsi_ctrl_pdata *ctrl) | |
+{ | |
+ struct mdss_panel_info *pinfo; | |
+ | |
+ if (!np || !ctrl) { | |
+ pr_err("%s: Invalid arguments\n", __func__); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ pinfo = &ctrl->panel_data.panel_info; | |
+ | |
+ pinfo->cont_splash_enabled = of_property_read_bool(np, | |
+ "qcom,cont-splash-enabled"); | |
+ | |
+ pinfo->partial_update_enabled = of_property_read_bool(np, | |
+ "qcom,partial-update-enabled"); | |
+ pr_info("%s:%d Partial update %s\n", __func__, __LINE__, | |
+ (pinfo->partial_update_enabled ? "enabled" : "disabled")); | |
+ if (pinfo->partial_update_enabled) | |
+ ctrl->partial_update_fnc = mdss_dsi_panel_partial_update; | |
+ | |
+ pinfo->ulps_feature_enabled = of_property_read_bool(np, | |
+ "qcom,ulps-enabled"); | |
+ pr_info("%s: ulps feature %s", __func__, | |
+ (pinfo->ulps_feature_enabled ? "enabled" : "disabled")); | |
+ pinfo->esd_check_enabled = of_property_read_bool(np, | |
+ "qcom,esd-check-enabled"); | |
+ | |
+ pinfo->mipi.dynamic_switch_enabled = of_property_read_bool(np, | |
+ "qcom,dynamic-mode-switch-enabled"); | |
+ | |
+ if (pinfo->mipi.dynamic_switch_enabled) { | |
+ mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd, | |
+ "qcom,video-to-cmd-mode-switch-commands", NULL); | |
+ | |
+ mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video, | |
+ "qcom,cmd-to-video-mode-switch-commands", NULL); | |
+ | |
+ if (!ctrl->video2cmd.cmd_cnt || !ctrl->cmd2video.cmd_cnt) { | |
+ pr_warn("No commands specified for dynamic switch\n"); | |
+ pinfo->mipi.dynamic_switch_enabled = 0; | |
+ } | |
+ } | |
+ | |
+ pr_info("%s: dynamic switch feature enabled: %d", __func__, | |
+ pinfo->mipi.dynamic_switch_enabled); | |
+ | |
+ return 0; | |
+} | |
static int mdss_panel_parse_dt(struct device_node *np, | |
struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
@@ -728,9 +915,11 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
tmp = 0; | |
data = of_get_property(np, "qcom,mdss-dsi-pixel-packing", NULL); | |
if (data && !strcmp(data, "loose")) | |
- tmp = 1; | |
- rc = mdss_panel_dt_get_dst_fmt(pinfo->bpp, | |
- pinfo->mipi.mode, tmp, | |
+ pinfo->mipi.pixel_packing = 1; | |
+ else | |
+ pinfo->mipi.pixel_packing = 0; | |
+ rc = mdss_panel_get_dst_fmt(pinfo->bpp, | |
+ pinfo->mipi.mode, pinfo->mipi.pixel_packing, | |
&(pinfo->mipi.dst_format)); | |
if (rc) { | |
pr_debug("%s: problem determining dst format. Set Default\n", | |
@@ -751,13 +940,14 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
else if (!strcmp(pdest, "display_2")) | |
pinfo->pdest = DISPLAY_2; | |
else { | |
- pr_debug("%s: pdest not specified. Set Default\n", | |
- __func__); | |
+ pr_debug("%s: incorrect pdest. Set Default\n", | |
+ __func__); | |
pinfo->pdest = DISPLAY_1; | |
} | |
} else { | |
- pr_err("%s: pdest not specified\n", __func__); | |
- return -EINVAL; | |
+ pr_debug("%s: pdest not specified. Set Default\n", | |
+ __func__); | |
+ pinfo->pdest = DISPLAY_1; | |
} | |
rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp); | |
pinfo->lcdc.h_front_porch = (!rc ? tmp : 6); | |
@@ -780,33 +970,6 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
"qcom,mdss-dsi-border-color", &tmp); | |
pinfo->lcdc.border_clr = (!rc ? tmp : 0); | |
pinfo->bklt_ctrl = UNKNOWN_CTRL; | |
- | |
- /* lcdc_tune is the same as lcdc by default, but can be overridden */ | |
- memcpy(&pinfo->lcdc_tune, &pinfo->lcdc, | |
- sizeof(struct lcd_panel_info)); | |
- | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-h-front-porch", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.h_front_porch = tmp; | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-h-back-porch", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.h_back_porch = tmp; | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-h-pulse-width", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.h_pulse_width = tmp; | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-h-sync-skew", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.hsync_skew = tmp; | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-v-back-porch", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.v_back_porch = tmp; | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-v-front-porch", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.v_front_porch = tmp; | |
- rc = of_property_read_u32(np, "qcom,mdss-dsi-tune-v-pulse-width", &tmp); | |
- if (!rc) | |
- pinfo->lcdc_tune.v_pulse_width = tmp; | |
- | |
data = of_get_property(np, "qcom,mdss-dsi-bl-pmic-control-type", NULL); | |
if (data) { | |
if (!strncmp(data, "bl_ctrl_wled", 12)) { | |
@@ -866,6 +1029,8 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
"qcom,mdss-dsi-hsa-power-mode"); | |
pinfo->mipi.hbp_power_stop = of_property_read_bool(np, | |
"qcom,mdss-dsi-hbp-power-mode"); | |
+ pinfo->mipi.last_line_interleave_en = of_property_read_bool(np, | |
+ "qcom,mdss-dsi-last-line-interleave"); | |
pinfo->mipi.bllp_power_stop = of_property_read_bool(np, | |
"qcom,mdss-dsi-bllp-power-mode"); | |
pinfo->mipi.eof_bllp_power_stop = of_property_read_bool( | |
@@ -883,11 +1048,11 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
pinfo->mipi.insert_dcs_cmd = | |
(!rc ? tmp : 1); | |
rc = of_property_read_u32(np, | |
- "qcom,mdss-dsi-te-v-sync-continue-lines", &tmp); | |
+ "qcom,mdss-dsi-wr-mem-continue", &tmp); | |
pinfo->mipi.wr_mem_continue = | |
(!rc ? tmp : 0x3c); | |
rc = of_property_read_u32(np, | |
- "qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line", &tmp); | |
+ "qcom,mdss-dsi-wr-mem-start", &tmp); | |
pinfo->mipi.wr_mem_start = | |
(!rc ? tmp : 0x2c); | |
rc = of_property_read_u32(np, | |
@@ -897,7 +1062,7 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
rc = of_property_read_u32(np, "qcom,mdss-dsi-virtual-channel-id", &tmp); | |
pinfo->mipi.vc = (!rc ? tmp : 0); | |
pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; | |
- data = of_get_property(np, "mdss-dsi-color-order", NULL); | |
+ data = of_get_property(np, "qcom,mdss-dsi-color-order", NULL); | |
if (data) { | |
if (!strcmp(data, "rgb_swap_rbg")) | |
pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RBG; | |
@@ -924,6 +1089,11 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp); | |
pinfo->mipi.t_clk_post = (!rc ? tmp : 0x03); | |
+ pinfo->mipi.rx_eot_ignore = of_property_read_bool(np, | |
+ "qcom,mdss-dsi-rx-eot-ignore"); | |
+ pinfo->mipi.tx_eot_append = of_property_read_bool(np, | |
+ "qcom,mdss-dsi-tx-eot-append"); | |
+ | |
rc = of_property_read_u32(np, "qcom,mdss-dsi-stream", &tmp); | |
pinfo->mipi.stream = (!rc ? tmp : 0); | |
@@ -956,6 +1126,7 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
pinfo->mipi.init_delay = (!rc ? tmp : 0); | |
mdss_dsi_parse_fbc_params(np, pinfo); | |
+ mdss_dsi_parse_roi_alignment(np, pinfo); | |
mdss_dsi_parse_trigger(np, &(pinfo->mipi.mdp_trigger), | |
"qcom,mdss-dsi-mdp-trigger"); | |
@@ -967,6 +1138,7 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len), | |
"qcom,mdss-dsi-reset-sequence"); | |
+ mdss_panel_parse_te_params(np, pinfo); | |
mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->on_cmds, | |
"qcom,mdss-dsi-on-command", "qcom,mdss-dsi-on-command-state"); | |
@@ -974,19 +1146,28 @@ static int mdss_panel_parse_dt(struct device_node *np, | |
mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds, | |
"qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state"); | |
-#ifdef CONFIG_LGE_MIPI_DSI_LGD_NT35521_WXGA | |
- ctrl_pdata->lcd_dsv_enp_gpio = of_get_named_gpio(np, "qcom,lcd_dsv_enp-gpio", 0); | |
- if(!gpio_is_valid(ctrl_pdata->lcd_dsv_enp_gpio)) { | |
- pr_err("%s: lcd_dsv_enp_gpio not specified\n" , __func__); | |
- goto error; | |
+ mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->status_cmds, | |
+ "qcom,mdss-dsi-panel-status-command", | |
+ "qcom,mdss-dsi-panel-status-command-state"); | |
+ rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-status-value", &tmp); | |
+ ctrl_pdata->status_value = (!rc ? tmp : 0); | |
+ | |
+ | |
+ ctrl_pdata->status_mode = ESD_MAX; | |
+ rc = of_property_read_string(np, | |
+ "qcom,mdss-dsi-panel-status-check-mode", &data); | |
+ if (!rc) { | |
+ if (!strcmp(data, "bta_check")) | |
+ ctrl_pdata->status_mode = ESD_BTA; | |
+ else if (!strcmp(data, "reg_read")) | |
+ ctrl_pdata->status_mode = ESD_REG; | |
} | |
- ctrl_pdata->lcd_dsv_enn_gpio = of_get_named_gpio(np, "qcom,lcd_dsv_enn-gpio", 0); | |
- if(!gpio_is_valid(ctrl_pdata->lcd_dsv_enn_gpio)) { | |
- pr_err("%s: lcd_dsv_enn_gpio not specified\n" , __func__); | |
+ rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); | |
+ if (rc) { | |
+ pr_err("%s: failed to parse panel features\n", __func__); | |
goto error; | |
} | |
-#endif | |
return 0; | |
@@ -1000,14 +1181,15 @@ int mdss_dsi_panel_init(struct device_node *node, | |
{ | |
int rc = 0; | |
static const char *panel_name; | |
- bool cont_splash_enabled; | |
- bool partial_update_enabled; | |
+ struct mdss_panel_info *pinfo; | |
- if (!node) { | |
- pr_err("%s: no panel node\n", __func__); | |
+ if (!node || !ctrl_pdata) { | |
+ pr_err("%s: Invalid arguments\n", __func__); | |
return -ENODEV; | |
} | |
+ pinfo = &ctrl_pdata->panel_data.panel_info; | |
+ | |
pr_debug("%s:%d\n", __func__, __LINE__); | |
panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); | |
if (!panel_name) | |
@@ -1022,37 +1204,18 @@ int mdss_dsi_panel_init(struct device_node *node, | |
return rc; | |
} | |
- if (cmd_cfg_cont_splash) | |
- cont_splash_enabled = of_property_read_bool(node, | |
- "qcom,cont-splash-enabled"); | |
- else | |
- cont_splash_enabled = false; | |
- if (!cont_splash_enabled) { | |
- pr_info("%s:%d Continuous splash flag not found.\n", | |
- __func__, __LINE__); | |
- ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 0; | |
- } else { | |
- pr_info("%s:%d Continuous splash flag enabled.\n", | |
- __func__, __LINE__); | |
+ if (!cmd_cfg_cont_splash) | |
+ pinfo->cont_splash_enabled = false; | |
+ pr_info("%s: Continuous splash %s", __func__, | |
+ pinfo->cont_splash_enabled ? "enabled" : "disabled"); | |
- ctrl_pdata->panel_data.panel_info.cont_splash_enabled = 1; | |
- } | |
- | |
- partial_update_enabled = of_property_read_bool(node, | |
- "qcom,partial-update-enabled"); | |
- if (partial_update_enabled) { | |
- pr_info("%s:%d Partial update enabled.\n", __func__, __LINE__); | |
- ctrl_pdata->panel_data.panel_info.partial_update_enabled = 1; | |
- ctrl_pdata->partial_update_fnc = mdss_dsi_panel_partial_update; | |
- } else { | |
- pr_info("%s:%d Partial update disabled.\n", __func__, __LINE__); | |
- ctrl_pdata->panel_data.panel_info.partial_update_enabled = 0; | |
- ctrl_pdata->partial_update_fnc = NULL; | |
- } | |
+ pinfo->dynamic_switch_pending = false; | |
+ pinfo->is_lpm_mode = false; | |
ctrl_pdata->on = mdss_dsi_panel_on; | |
ctrl_pdata->off = mdss_dsi_panel_off; | |
ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; | |
+ ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; | |
return 0; | |
} | |
diff --git a/drivers/video/msm/mdss/mdss_dsi_status.c b/drivers/video/msm/mdss/mdss_dsi_status.c | |
index f0c4f4c..02540bb 100644 | |
--- a/drivers/video/msm/mdss/mdss_dsi_status.c | |
+++ b/drivers/video/msm/mdss/mdss_dsi_status.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2013-2014, 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 | |
@@ -30,99 +30,37 @@ | |
#include "mdss_panel.h" | |
#include "mdss_mdp.h" | |
-#define STATUS_CHECK_INTERVAL 5000 | |
+#define STATUS_CHECK_INTERVAL_MS 5000 | |
+#define STATUS_CHECK_INTERVAL_MIN_MS 200 | |
+#define DSI_STATUS_CHECK_DISABLE 0 | |
-struct dsi_status_data { | |
- struct notifier_block fb_notifier; | |
- struct delayed_work check_status; | |
- struct msm_fb_data_type *mfd; | |
- uint32_t check_interval; | |
-}; | |
+static uint32_t interval = STATUS_CHECK_INTERVAL_MS; | |
+static uint32_t dsi_status_disable = DSI_STATUS_CHECK_DISABLE; | |
struct dsi_status_data *pstatus_data; | |
-static uint32_t interval = STATUS_CHECK_INTERVAL; | |
/* | |
- * check_dsi_ctrl_status() - Check DSI controller status periodically. | |
+ * check_dsi_ctrl_status() - Reads MFD structure and | |
+ * calls platform specific DSI ctrl Status function. | |
* @work : dsi controller status data | |
- * | |
- * This function calls check_status API on DSI controller to send the BTA | |
- * command. If DSI controller fails to acknowledge the BTA command, it sends | |
- * the PANEL_ALIVE=0 status to HAL layer. | |
*/ | |
static void check_dsi_ctrl_status(struct work_struct *work) | |
{ | |
struct dsi_status_data *pdsi_status = NULL; | |
- struct mdss_panel_data *pdata = NULL; | |
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
- struct mdss_overlay_private *mdp5_data = NULL; | |
- struct mdss_mdp_ctl *ctl = NULL; | |
- int ret = 0; | |
pdsi_status = container_of(to_delayed_work(work), | |
struct dsi_status_data, check_status); | |
+ | |
if (!pdsi_status) { | |
pr_err("%s: DSI status data not available\n", __func__); | |
return; | |
} | |
- pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev); | |
- if (!pdata) { | |
- pr_err("%s: Panel data not available\n", __func__); | |
+ if (!pdsi_status->mfd) { | |
+ pr_err("%s: FB data not available\n", __func__); | |
return; | |
} | |
- ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
- panel_data); | |
- if (!ctrl_pdata || !ctrl_pdata->check_status) { | |
- pr_err("%s: DSI ctrl or status_check callback not available\n", | |
- __func__); | |
- return; | |
- } | |
- | |
- mdp5_data = mfd_to_mdp5_data(pdsi_status->mfd); | |
- ctl = mfd_to_ctl(pdsi_status->mfd); | |
- | |
- if (ctl->shared_lock) | |
- mutex_lock(ctl->shared_lock); | |
- mutex_lock(&mdp5_data->ov_lock); | |
- | |
- /* | |
- * For the command mode panels, we return pan display | |
- * IOCTL on vsync interrupt. So, after vsync interrupt comes | |
- * and when DMA_P is in progress, if the panel stops responding | |
- * and if we trigger BTA before DMA_P finishes, then the DSI | |
- * FIFO will not be cleared since the DSI data bus control | |
- * doesn't come back to the host after BTA. This may cause the | |
- * display reset not to be proper. Hence, wait for DMA_P done | |
- * for command mode panels before triggering BTA. | |
- */ | |
- if (ctl->wait_pingpong) | |
- ctl->wait_pingpong(ctl, NULL); | |
- | |
- pr_debug("%s: DSI ctrl wait for ping pong done\n", __func__); | |
- | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
- ret = ctrl_pdata->check_status(ctrl_pdata); | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
- | |
- mutex_unlock(&mdp5_data->ov_lock); | |
- if (ctl->shared_lock) | |
- mutex_unlock(ctl->shared_lock); | |
- | |
- if ((pdsi_status->mfd->panel_power_on)) { | |
- if (ret > 0) { | |
- schedule_delayed_work(&pdsi_status->check_status, | |
- msecs_to_jiffies(pdsi_status->check_interval)); | |
- } else { | |
- char *envp[2] = {"PANEL_ALIVE=0", NULL}; | |
- pdata->panel_info.panel_dead = true; | |
- ret = kobject_uevent_env( | |
- &pdsi_status->mfd->fbi->dev->kobj, | |
- KOBJ_CHANGE, envp); | |
- pr_err("%s: Panel has gone bad, sending uevent - %s\n", | |
- __func__, envp[0]); | |
- } | |
- } | |
+ pdsi_status->mfd->mdp.check_dsi_status(work, interval); | |
} | |
/* | |
@@ -142,23 +80,89 @@ static int fb_event_callback(struct notifier_block *self, | |
struct fb_event *evdata = data; | |
struct dsi_status_data *pdata = container_of(self, | |
struct dsi_status_data, fb_notifier); | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ struct mdss_panel_info *pinfo; | |
+ | |
pdata->mfd = evdata->info->par; | |
+ ctrl_pdata = container_of(dev_get_platdata(&pdata->mfd->pdev->dev), | |
+ struct mdss_dsi_ctrl_pdata, panel_data); | |
+ if (!ctrl_pdata) { | |
+ pr_err("%s: DSI ctrl not available\n", __func__); | |
+ return NOTIFY_BAD; | |
+ } | |
+ | |
+ pinfo = &ctrl_pdata->panel_data.panel_info; | |
+ | |
+ if (!(pinfo->esd_check_enabled)) { | |
+ pr_debug("ESD check is not enaled in panel dtsi\n"); | |
+ return NOTIFY_DONE; | |
+ } | |
+ | |
+ if (dsi_status_disable) { | |
+ pr_debug("%s: DSI status disabled\n", __func__); | |
+ return NOTIFY_DONE; | |
+ } | |
if (event == FB_EVENT_BLANK && evdata) { | |
int *blank = evdata->data; | |
+ struct dsi_status_data *pdata = container_of(self, | |
+ struct dsi_status_data, fb_notifier); | |
+ pdata->mfd = evdata->info->par; | |
+ | |
switch (*blank) { | |
case FB_BLANK_UNBLANK: | |
schedule_delayed_work(&pdata->check_status, | |
- msecs_to_jiffies(pdata->check_interval)); | |
+ msecs_to_jiffies(interval)); | |
break; | |
case FB_BLANK_POWERDOWN: | |
+ case FB_BLANK_HSYNC_SUSPEND: | |
+ case FB_BLANK_VSYNC_SUSPEND: | |
+ case FB_BLANK_NORMAL: | |
cancel_delayed_work(&pdata->check_status); | |
break; | |
+ default: | |
+ pr_err("Unknown case in FB_EVENT_BLANK event\n"); | |
+ break; | |
} | |
} | |
return 0; | |
} | |
+static int param_dsi_status_disable(const char *val, struct kernel_param *kp) | |
+{ | |
+ int ret = 0; | |
+ int int_val; | |
+ | |
+ ret = kstrtos32(val, 0, &int_val); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ pr_info("%s: Set DSI status disable to %d\n", | |
+ __func__, int_val); | |
+ *((int *)kp->arg) = int_val; | |
+ return ret; | |
+} | |
+ | |
+static int param_set_interval(const char *val, struct kernel_param *kp) | |
+{ | |
+ int ret = 0; | |
+ int int_val; | |
+ | |
+ ret = kstrtos32(val, 0, &int_val); | |
+ if (ret) | |
+ return ret; | |
+ if (int_val < STATUS_CHECK_INTERVAL_MIN_MS) { | |
+ pr_err("%s: Invalid value %d used, ignoring\n", | |
+ __func__, int_val); | |
+ ret = -EINVAL; | |
+ } else { | |
+ pr_info("%s: Set check interval to %d msecs\n", | |
+ __func__, int_val); | |
+ *((int *)kp->arg) = int_val; | |
+ } | |
+ return ret; | |
+} | |
+ | |
int __init mdss_dsi_status_init(void) | |
{ | |
int rc = 0; | |
@@ -179,7 +183,6 @@ int __init mdss_dsi_status_init(void) | |
return -EPERM; | |
} | |
- pstatus_data->check_interval = interval; | |
pr_info("%s: DSI status check interval:%d\n", __func__, interval); | |
INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status); | |
@@ -197,11 +200,17 @@ void __exit mdss_dsi_status_exit(void) | |
pr_debug("%s: DSI ctrl status work queue removed\n", __func__); | |
} | |
-module_param(interval, uint, 0); | |
+module_param_call(interval, param_set_interval, param_get_uint, | |
+ &interval, 0644); | |
MODULE_PARM_DESC(interval, | |
"Duration in milliseconds to send BTA command for checking" | |
"DSI status periodically"); | |
+module_param_call(dsi_status_disable, param_dsi_status_disable, param_get_uint, | |
+ &dsi_status_disable, 0644); | |
+MODULE_PARM_DESC(dsi_status_disable, | |
+ "Disable DSI status check"); | |
+ | |
module_init(mdss_dsi_status_init); | |
module_exit(mdss_dsi_status_exit); | |
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c | |
index 476803c..db66761 100644 | |
--- a/drivers/video/msm/mdss/mdss_fb.c | |
+++ b/drivers/video/msm/mdss/mdss_fb.c | |
@@ -2,7 +2,7 @@ | |
* Core MDSS framebuffer driver. | |
* | |
* Copyright (C) 2007 Google Incorporated | |
- * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved. | |
+ * Copyright (c) 2008-2015, The Linux Foundation. All rights reserved. | |
* | |
* This software is licensed under the terms of the GNU General Public | |
* License version 2, as published by the Free Software Foundation, and | |
@@ -44,6 +44,7 @@ | |
#include <linux/file.h> | |
#include <linux/memory_alloc.h> | |
#include <linux/kthread.h> | |
+#include <linux/of_address.h> | |
#include <mach/board.h> | |
#include <mach/memory.h> | |
@@ -52,13 +53,7 @@ | |
#include <mach/msm_memtypes.h> | |
#include "mdss_fb.h" | |
- | |
-#ifdef CONFIG_MACH_LGE | |
-#include "mdss_mdp.h" | |
-#include <mach/board_lge.h> | |
-static int force_set_bl_f; | |
-unsigned long msm_fb_phys_addr_backup; | |
-#endif | |
+#include "mdss_mdp_splash_logo.h" | |
#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER | |
#define MDSS_FB_NUM 3 | |
@@ -93,7 +88,10 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd); | |
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
unsigned long arg); | |
-static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma); | |
+static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, | |
+ struct vm_area_struct *vma); | |
+static int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, | |
+ size_t size); | |
static void mdss_fb_release_fences(struct msm_fb_data_type *mfd); | |
static int __mdss_fb_sync_buf_done_callback(struct notifier_block *p, | |
unsigned long val, void *data); | |
@@ -102,6 +100,7 @@ static int __mdss_fb_display_thread(void *data); | |
static int mdss_fb_pan_idle(struct msm_fb_data_type *mfd); | |
static int mdss_fb_send_panel_event(struct msm_fb_data_type *mfd, | |
int event, void *arg); | |
+static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd); | |
void mdss_fb_no_update_notify_timer_cb(unsigned long data) | |
{ | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; | |
@@ -113,6 +112,31 @@ void mdss_fb_no_update_notify_timer_cb(unsigned long data) | |
complete(&mfd->no_update.comp); | |
} | |
+void mdss_fb_bl_update_notify(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) { | |
+ pr_err("%s mfd NULL\n", __func__); | |
+ return; | |
+ } | |
+ mutex_lock(&mfd->update.lock); | |
+ if (mfd->update.ref_count > 0) { | |
+ mutex_unlock(&mfd->update.lock); | |
+ mfd->update.value = NOTIFY_TYPE_BL_UPDATE; | |
+ complete(&mfd->update.comp); | |
+ mutex_lock(&mfd->update.lock); | |
+ } | |
+ mutex_unlock(&mfd->update.lock); | |
+ | |
+ mutex_lock(&mfd->no_update.lock); | |
+ if (mfd->no_update.ref_count > 0) { | |
+ mutex_unlock(&mfd->no_update.lock); | |
+ mfd->no_update.value = NOTIFY_TYPE_BL_UPDATE; | |
+ complete(&mfd->no_update.comp); | |
+ mutex_lock(&mfd->no_update.lock); | |
+ } | |
+ mutex_unlock(&mfd->no_update.lock); | |
+} | |
+ | |
static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, | |
unsigned long *argp) | |
{ | |
@@ -128,10 +152,20 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, | |
if (notify > NOTIFY_UPDATE_POWER_OFF) | |
return -EINVAL; | |
- if (notify == NOTIFY_UPDATE_START) { | |
+ if (mfd->update.is_suspend) { | |
+ to_user = NOTIFY_TYPE_SUSPEND; | |
+ mfd->update.is_suspend = 0; | |
+ ret = 1; | |
+ } else if (notify == NOTIFY_UPDATE_START) { | |
INIT_COMPLETION(mfd->update.comp); | |
- ret = wait_for_completion_timeout( | |
+ mutex_lock(&mfd->update.lock); | |
+ mfd->update.ref_count++; | |
+ mutex_unlock(&mfd->update.lock); | |
+ ret = wait_for_completion_interruptible_timeout( | |
&mfd->update.comp, 4 * HZ); | |
+ mutex_lock(&mfd->update.lock); | |
+ mfd->update.ref_count--; | |
+ mutex_unlock(&mfd->update.lock); | |
to_user = (unsigned int)mfd->update.value; | |
if (mfd->update.type == NOTIFY_TYPE_SUSPEND) { | |
to_user = (unsigned int)mfd->update.type; | |
@@ -139,13 +173,19 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, | |
} | |
} else if (notify == NOTIFY_UPDATE_STOP) { | |
INIT_COMPLETION(mfd->no_update.comp); | |
- ret = wait_for_completion_timeout( | |
+ mutex_lock(&mfd->no_update.lock); | |
+ mfd->no_update.ref_count++; | |
+ mutex_unlock(&mfd->no_update.lock); | |
+ ret = wait_for_completion_interruptible_timeout( | |
&mfd->no_update.comp, 4 * HZ); | |
+ mutex_lock(&mfd->no_update.lock); | |
+ mfd->no_update.ref_count--; | |
+ mutex_unlock(&mfd->no_update.lock); | |
to_user = (unsigned int)mfd->no_update.value; | |
} else { | |
if (mfd->panel_power_on) { | |
INIT_COMPLETION(mfd->power_off_comp); | |
- ret = wait_for_completion_timeout( | |
+ ret = wait_for_completion_interruptible_timeout( | |
&mfd->power_off_comp, 1 * HZ); | |
} | |
} | |
@@ -230,23 +270,32 @@ static ssize_t mdss_fb_get_type(struct device *dev, | |
return ret; | |
} | |
-static void mdss_fb_parse_dt_split(struct msm_fb_data_type *mfd) | |
+static void mdss_fb_parse_dt(struct msm_fb_data_type *mfd) | |
{ | |
- u32 data[2]; | |
+ u32 data[2] = {0}; | |
+ u32 panel_xres; | |
struct platform_device *pdev = mfd->pdev; | |
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,mdss-fb-split", | |
- data, 2)) | |
- return; | |
- if (data[0] && data[1] && | |
- (mfd->panel_info->xres == (data[0] + data[1]))) { | |
- mfd->split_fb_left = data[0]; | |
- mfd->split_fb_right = data[1]; | |
- pr_info("split framebuffer left=%d right=%d\n", | |
- mfd->split_fb_left, mfd->split_fb_right); | |
+ | |
+ of_property_read_u32_array(pdev->dev.of_node, | |
+ "qcom,mdss-fb-split", data, 2); | |
+ | |
+ panel_xres = mfd->panel_info->xres; | |
+ if (data[0] && data[1]) { | |
+ if (mfd->split_display) | |
+ panel_xres *= 2; | |
+ | |
+ if (panel_xres == data[0] + data[1]) { | |
+ mfd->split_fb_left = data[0]; | |
+ mfd->split_fb_right = data[1]; | |
+ } | |
} else { | |
- mfd->split_fb_left = 0; | |
- mfd->split_fb_right = 0; | |
+ if (mfd->split_display) | |
+ mfd->split_fb_left = mfd->split_fb_right = panel_xres; | |
+ else | |
+ mfd->split_fb_left = mfd->split_fb_right = 0; | |
} | |
+ pr_info("split framebuffer left=%d right=%d\n", | |
+ mfd->split_fb_left, mfd->split_fb_right); | |
} | |
static ssize_t mdss_fb_get_split(struct device *dev, | |
@@ -330,12 +379,117 @@ static ssize_t mdss_fb_get_idle_notify(struct device *dev, | |
return ret; | |
} | |
+static ssize_t mdss_fb_get_panel_info(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct fb_info *fbi = dev_get_drvdata(dev); | |
+ struct msm_fb_data_type *mfd = fbi->par; | |
+ struct mdss_panel_info *pinfo = mfd->panel_info; | |
+ int ret; | |
+ | |
+ ret = scnprintf(buf, PAGE_SIZE, | |
+ "pu_en=%d\nxstart=%d\nwalign=%d\nystart=%d\nhalign=%d\n" | |
+ "min_w=%d\nmin_h=%d", | |
+ pinfo->partial_update_enabled, pinfo->xstart_pix_align, | |
+ pinfo->width_pix_align, pinfo->ystart_pix_align, | |
+ pinfo->height_pix_align, pinfo->min_width, | |
+ pinfo->min_height); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/* | |
+ * mdss_fb_lpm_enable() - Function to Control LowPowerMode | |
+ * @mfd: Framebuffer data structure for display | |
+ * @mode: Enabled/Disable LowPowerMode | |
+ * 1: Enter into LowPowerMode | |
+ * 0: Exit from LowPowerMode | |
+ * | |
+ * This Function dynamically switches to and from LowPowerMode | |
+ * based on the argument @mode. | |
+ */ | |
+static int mdss_fb_lpm_enable(struct msm_fb_data_type *mfd, int mode) | |
+{ | |
+ int ret = 0; | |
+ u32 bl_lvl = 0; | |
+ struct mdss_panel_info *pinfo = NULL; | |
+ struct mdss_panel_data *pdata; | |
+ | |
+ if (!mfd || !mfd->panel_info) | |
+ return -EINVAL; | |
+ | |
+ pinfo = mfd->panel_info; | |
+ | |
+ if (!pinfo->mipi.dynamic_switch_enabled) { | |
+ pr_warn("Panel does not support dynamic switch!\n"); | |
+ return 0; | |
+ } | |
+ | |
+ if (mode == pinfo->mipi.mode) { | |
+ pr_debug("Already in requested mode!\n"); | |
+ return 0; | |
+ } | |
+ | |
+ pdata = dev_get_platdata(&mfd->pdev->dev); | |
+ | |
+ pr_debug("Enter mode: %d\n", mode); | |
+ pdata->panel_info.dynamic_switch_pending = true; | |
+ | |
+ mutex_lock(&mfd->bl_lock); | |
+ bl_lvl = mfd->bl_level; | |
+ mdss_fb_set_backlight(mfd, 0); | |
+ mutex_unlock(&mfd->bl_lock); | |
+ | |
+ lock_fb_info(mfd->fbi); | |
+ ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, | |
+ mfd->op_enable); | |
+ if (ret) { | |
+ pr_err("can't turn off display!\n"); | |
+ unlock_fb_info(mfd->fbi); | |
+ return ret; | |
+ } | |
+ | |
+ mfd->op_enable = false; | |
+ | |
+ ret = mfd->mdp.configure_panel(mfd, mode); | |
+ mdss_fb_set_mdp_sync_pt_threshold(mfd); | |
+ | |
+ mfd->op_enable = true; | |
+ | |
+ ret = mdss_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi, | |
+ mfd->op_enable); | |
+ if (ret) { | |
+ pr_err("can't turn on display!\n"); | |
+ unlock_fb_info(mfd->fbi); | |
+ return ret; | |
+ } | |
+ unlock_fb_info(mfd->fbi); | |
+ | |
+ mutex_lock(&mfd->bl_lock); | |
+ mfd->bl_updated = true; | |
+ mdss_fb_set_backlight(mfd, bl_lvl); | |
+ mutex_unlock(&mfd->bl_lock); | |
+ | |
+ pdata->panel_info.dynamic_switch_pending = false; | |
+ pdata->panel_info.is_lpm_mode = mode ? 1 : 0; | |
+ | |
+ if (ret) { | |
+ pr_err("can't turn on display!\n"); | |
+ return ret; | |
+ } | |
+ | |
+ pr_debug("Exit mode: %d\n", mode); | |
+ | |
+ return 0; | |
+} | |
+ | |
static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL); | |
static DEVICE_ATTR(msm_fb_split, S_IRUGO, mdss_fb_get_split, NULL); | |
static DEVICE_ATTR(show_blank_event, S_IRUGO, mdss_mdp_show_blank_event, NULL); | |
static DEVICE_ATTR(idle_time, S_IRUGO | S_IWUSR | S_IWGRP, | |
mdss_fb_get_idle_time, mdss_fb_set_idle_time); | |
static DEVICE_ATTR(idle_notify, S_IRUGO, mdss_fb_get_idle_notify, NULL); | |
+static DEVICE_ATTR(msm_fb_panel_info, S_IRUGO, mdss_fb_get_panel_info, NULL); | |
static struct attribute *mdss_fb_attrs[] = { | |
&dev_attr_msm_fb_type.attr, | |
@@ -343,6 +497,7 @@ static struct attribute *mdss_fb_attrs[] = { | |
&dev_attr_show_blank_event.attr, | |
&dev_attr_idle_time.attr, | |
&dev_attr_idle_notify.attr, | |
+ &dev_attr_msm_fb_panel_info.attr, | |
NULL, | |
}; | |
@@ -410,8 +565,10 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
mfd->ext_ad_ctrl = -1; | |
mfd->bl_level = 0; | |
+ mfd->bl_level_prev_scaled = 0; | |
mfd->bl_scale = 1024; | |
mfd->bl_min_lvl = 30; | |
+ mfd->ad_bl_level = 0; | |
mfd->fb_imgType = MDP_RGBA_8888; | |
mfd->pdev = pdev; | |
@@ -420,7 +577,6 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
mfd->mdp = *mdp_instance; | |
INIT_LIST_HEAD(&mfd->proc_list); | |
- mutex_init(&mfd->lock); | |
mutex_init(&mfd->bl_lock); | |
fbi_list[fbi_list_index++] = fbi; | |
@@ -473,6 +629,21 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
__mdss_fb_sync_buf_done_callback; | |
} | |
+ mdss_fb_set_mdp_sync_pt_threshold(mfd); | |
+ | |
+ if (mfd->mdp.splash_init_fnc) | |
+ mfd->mdp.splash_init_fnc(mfd); | |
+ | |
+ INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work); | |
+ | |
+ return rc; | |
+} | |
+ | |
+static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) | |
+ return; | |
+ | |
switch (mfd->panel.type) { | |
case WRITEBACK_PANEL: | |
mfd->mdp_sync_pt_data.threshold = 1; | |
@@ -487,25 +658,20 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
mfd->mdp_sync_pt_data.retire_threshold = 0; | |
break; | |
} | |
- | |
- INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work); | |
- | |
- return rc; | |
} | |
- | |
static int mdss_fb_remove(struct platform_device *pdev) | |
{ | |
struct msm_fb_data_type *mfd; | |
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); | |
+ if (!mfd) | |
+ return -ENODEV; | |
+ | |
mdss_fb_remove_sysfs(mfd); | |
pm_runtime_disable(mfd->fbi->dev); | |
- if (!mfd) | |
- return -ENODEV; | |
- | |
if (mfd->key != MFD_KEY) | |
return -EINVAL; | |
@@ -721,26 +887,12 @@ static void mdss_fb_scale_bl(struct msm_fb_data_type *mfd, u32 *bl_lvl) | |
void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
{ | |
struct mdss_panel_data *pdata; | |
- int (*update_ad_input)(struct msm_fb_data_type *mfd); | |
u32 temp = bkl_lvl; | |
+ bool bl_notify_needed = false; | |
-#ifdef CONFIG_MACH_LGE | |
- if(force_set_bl_f || lge_get_boot_mode()== LGE_BOOT_MODE_QEM_130K) { | |
- | |
- pdata = dev_get_platdata(&mfd->pdev->dev); | |
- | |
- if ((pdata) && (pdata->set_backlight)) { | |
- mdss_fb_scale_bl(mfd, &temp); | |
- pdata->set_backlight(pdata, temp); | |
- pr_info("regardless bl_updated, force to set bl. level=%d, laf_mode=%d\n", | |
- temp, lge_get_laf_mode()); | |
- } | |
- return; | |
- } | |
-#endif | |
- | |
- if (((!mfd->panel_power_on && mfd->dcm_state != DCM_ENTER) | |
- || !mfd->bl_updated) && !IS_CALIB_MODE_BL(mfd)) { | |
+ if ((((!mfd->panel_power_on && mfd->dcm_state != DCM_ENTER) | |
+ || !mfd->bl_updated) && !IS_CALIB_MODE_BL(mfd)) || | |
+ mfd->panel_info->cont_splash_enabled) { | |
mfd->unset_bl_level = bkl_lvl; | |
return; | |
} else { | |
@@ -750,6 +902,13 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
pdata = dev_get_platdata(&mfd->pdev->dev); | |
if ((pdata) && (pdata->set_backlight)) { | |
+ if (mfd->mdp.ad_calc_bl) | |
+ (*mfd->mdp.ad_calc_bl)(mfd, temp, &temp, | |
+ &bl_notify_needed); | |
+ if (bl_notify_needed) | |
+ mdss_fb_bl_update_notify(mfd); | |
+ | |
+ mfd->bl_level_prev_scaled = mfd->bl_level_scaled; | |
if (!IS_CALIB_MODE_BL(mfd)) | |
mdss_fb_scale_bl(mfd, &temp); | |
/* | |
@@ -760,20 +919,13 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
* as well as setting bl_level to bkl_lvl even though the | |
* backlight has been set to the scaled value. | |
*/ | |
- if (mfd->bl_level_old == temp) { | |
+ if (mfd->bl_level_scaled == temp) { | |
mfd->bl_level = bkl_lvl; | |
- return; | |
- } | |
- pdata->set_backlight(pdata, temp); | |
- mfd->bl_level = bkl_lvl; | |
- mfd->bl_level_old = temp; | |
- | |
- if (mfd->mdp.update_ad_input) { | |
- update_ad_input = mfd->mdp.update_ad_input; | |
- mutex_unlock(&mfd->bl_lock); | |
- /* Will trigger ad_setup which will grab bl_lock */ | |
- update_ad_input(mfd); | |
- mutex_lock(&mfd->bl_lock); | |
+ } else { | |
+ pr_debug("backlight sent to panel :%d\n", temp); | |
+ pdata->set_backlight(pdata, temp); | |
+ mfd->bl_level = bkl_lvl; | |
+ mfd->bl_level_scaled = temp; | |
} | |
} | |
} | |
@@ -781,18 +933,26 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
void mdss_fb_update_backlight(struct msm_fb_data_type *mfd) | |
{ | |
struct mdss_panel_data *pdata; | |
+ u32 temp; | |
+ bool bl_notify = false; | |
+ mutex_lock(&mfd->bl_lock); | |
if (mfd->unset_bl_level && !mfd->bl_updated) { | |
pdata = dev_get_platdata(&mfd->pdev->dev); | |
if ((pdata) && (pdata->set_backlight)) { | |
- mutex_lock(&mfd->bl_lock); | |
mfd->bl_level = mfd->unset_bl_level; | |
- pdata->set_backlight(pdata, mfd->bl_level); | |
- mfd->bl_level_old = mfd->unset_bl_level; | |
- mutex_unlock(&mfd->bl_lock); | |
+ temp = mfd->bl_level; | |
+ if (mfd->mdp.ad_calc_bl) | |
+ (*mfd->mdp.ad_calc_bl)(mfd, temp, &temp, | |
+ &bl_notify); | |
+ if (bl_notify) | |
+ mdss_fb_bl_update_notify(mfd); | |
+ pdata->set_backlight(pdata, temp); | |
+ mfd->bl_level_scaled = mfd->unset_bl_level; | |
mfd->bl_updated = 1; | |
} | |
} | |
+ mutex_unlock(&mfd->bl_lock); | |
} | |
static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
@@ -817,6 +977,7 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
} | |
mutex_lock(&mfd->update.lock); | |
mfd->update.type = NOTIFY_TYPE_UPDATE; | |
+ mfd->update.is_suspend = 0; | |
mutex_unlock(&mfd->update.lock); | |
/* Start the work thread to signal idle time */ | |
@@ -824,6 +985,13 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
schedule_delayed_work(&mfd->idle_notify_work, | |
msecs_to_jiffies(mfd->idle_time)); | |
} | |
+ | |
+ mutex_lock(&mfd->bl_lock); | |
+ if (!mfd->bl_updated) { | |
+ mfd->bl_updated = 1; | |
+ mdss_fb_set_backlight(mfd, mfd->bl_level_prev_scaled); | |
+ } | |
+ mutex_unlock(&mfd->bl_lock); | |
break; | |
case FB_BLANK_VSYNC_SUSPEND: | |
@@ -836,15 +1004,20 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
mutex_lock(&mfd->update.lock); | |
mfd->update.type = NOTIFY_TYPE_SUSPEND; | |
+ mfd->update.is_suspend = 1; | |
mutex_unlock(&mfd->update.lock); | |
+ complete(&mfd->update.comp); | |
del_timer(&mfd->no_update.timer); | |
mfd->no_update.value = NOTIFY_TYPE_SUSPEND; | |
complete(&mfd->no_update.comp); | |
mfd->op_enable = false; | |
curr_pwr_state = mfd->panel_power_on; | |
+ mutex_lock(&mfd->bl_lock); | |
+ mdss_fb_set_backlight(mfd, 0); | |
mfd->panel_power_on = false; | |
mfd->bl_updated = 0; | |
+ mutex_unlock(&mfd->bl_lock); | |
ret = mfd->mdp.off_fnc(mfd); | |
if (ret) | |
@@ -856,12 +1029,15 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
} | |
break; | |
} | |
+ /* Notify listeners */ | |
+ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event"); | |
return ret; | |
} | |
static int mdss_fb_blank(int blank_mode, struct fb_info *info) | |
{ | |
+ struct mdss_panel_data *pdata; | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
mdss_fb_pan_idle(mfd); | |
@@ -872,71 +1048,297 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) | |
mfd->suspend.panel_power_on = false; | |
return 0; | |
} | |
+ pr_debug("mode: %d\n", blank_mode); | |
+ | |
+ pdata = dev_get_platdata(&mfd->pdev->dev); | |
+ | |
+ if (pdata->panel_info.is_lpm_mode && | |
+ blank_mode == FB_BLANK_UNBLANK) { | |
+ pr_debug("panel is in lpm mode\n"); | |
+ mfd->mdp.configure_panel(mfd, 0); | |
+ mdss_fb_set_mdp_sync_pt_threshold(mfd); | |
+ pdata->panel_info.is_lpm_mode = false; | |
+ } | |
+ | |
return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); | |
} | |
+static inline int mdss_fb_create_ion_client(struct msm_fb_data_type *mfd) | |
+{ | |
+ mfd->fb_ion_client = msm_ion_client_create(-1 , "mdss_fb_iclient"); | |
+ if (IS_ERR_OR_NULL(mfd->fb_ion_client)) { | |
+ pr_err("Err:client not created, val %d\n", | |
+ PTR_RET(mfd->fb_ion_client)); | |
+ mfd->fb_ion_client = NULL; | |
+ return PTR_RET(mfd->fb_ion_client); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+void mdss_fb_free_fb_ion_memory(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) { | |
+ pr_err("no mfd\n"); | |
+ return; | |
+ } | |
+ | |
+ if (!mfd->fbi->screen_base) | |
+ return; | |
+ | |
+ if (!mfd->fb_ion_client || !mfd->fb_ion_handle) { | |
+ pr_err("invalid input parameters for fb%d\n", mfd->index); | |
+ return; | |
+ } | |
+ | |
+ mfd->fbi->screen_base = NULL; | |
+ mfd->fbi->fix.smem_start = 0; | |
+ | |
+ ion_unmap_kernel(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ | |
+ if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ ion_unmap_iommu(mfd->fb_ion_client, mfd->fb_ion_handle, | |
+ mfd->mdp.fb_mem_get_iommu_domain(), 0); | |
+ } | |
+ | |
+ ion_free(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ mfd->fb_ion_handle = NULL; | |
+} | |
+ | |
+int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, size_t fb_size) | |
+{ | |
+ unsigned long buf_size; | |
+ int rc; | |
+ void *vaddr; | |
+ | |
+ if (!mfd) { | |
+ pr_err("Invalid input param - no mfd"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (!mfd->fb_ion_client) { | |
+ rc = mdss_fb_create_ion_client(mfd); | |
+ if (rc < 0) { | |
+ pr_err("fb ion client couldn't be created - %d\n", rc); | |
+ return rc; | |
+ } | |
+ } | |
+ | |
+ pr_debug("size for mmap = %zu", fb_size); | |
+ mfd->fb_ion_handle = ion_alloc(mfd->fb_ion_client, fb_size, SZ_4K, | |
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0); | |
+ if (IS_ERR_OR_NULL(mfd->fb_ion_handle)) { | |
+ pr_err("unable to alloc fbmem from ion - %ld\n", | |
+ PTR_ERR(mfd->fb_ion_handle)); | |
+ return PTR_ERR(mfd->fb_ion_handle); | |
+ } | |
+ | |
+ if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ rc = ion_map_iommu(mfd->fb_ion_client, mfd->fb_ion_handle, | |
+ mfd->mdp.fb_mem_get_iommu_domain(), 0, SZ_4K, 0, | |
+ &mfd->iova, &buf_size, 0, 0); | |
+ if (rc) { | |
+ pr_err("Cannot map fb_mem to IOMMU. rc=%d\n", rc); | |
+ goto fb_mmap_failed; | |
+ } | |
+ } else { | |
+ pr_err("No IOMMU Domain"); | |
+ goto fb_mmap_failed; | |
+ | |
+ } | |
+ | |
+ vaddr = ion_map_kernel(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ if (IS_ERR_OR_NULL(vaddr)) { | |
+ pr_err("ION memory mapping failed - %ld\n", PTR_ERR(vaddr)); | |
+ rc = PTR_ERR(vaddr); | |
+ if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ ion_unmap_iommu(mfd->fb_ion_client, mfd->fb_ion_handle, | |
+ mfd->mdp.fb_mem_get_iommu_domain(), 0); | |
+ } | |
+ goto fb_mmap_failed; | |
+ } | |
+ | |
+ pr_debug("alloc 0x%zuB vaddr = %p (%pa iova) for fb%d\n", fb_size, | |
+ vaddr, &mfd->iova, mfd->index); | |
+ | |
+ mfd->fbi->screen_base = (char *) vaddr; | |
+ mfd->fbi->fix.smem_start = (unsigned int) mfd->iova; | |
+ mfd->fbi->fix.smem_len = fb_size; | |
+ | |
+ return rc; | |
+ | |
+fb_mmap_failed: | |
+ ion_free(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ return rc; | |
+} | |
+ | |
+/** | |
+ * mdss_fb_fbmem_ion_mmap() - Custom fb mmap() function for MSM driver. | |
+ * | |
+ * @info - Framebuffer info. | |
+ * @vma - VM area which is part of the process virtual memory. | |
+ * | |
+ * This framebuffer mmap function differs from standard mmap() function by | |
+ * allowing for customized page-protection and dynamically allocate framebuffer | |
+ * memory from system heap and map to iommu virtual address. | |
+ * | |
+ * Return: virtual address is returned through vma | |
+ */ | |
+static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, | |
+ struct vm_area_struct *vma) | |
+{ | |
+ int rc = 0; | |
+ size_t req_size, fb_size; | |
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
+ struct sg_table *table; | |
+ unsigned long addr = vma->vm_start; | |
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE; | |
+ struct scatterlist *sg; | |
+ unsigned int i; | |
+ struct page *page; | |
+ | |
+ if (!mfd || !mfd->pdev || !mfd->pdev->dev.of_node) { | |
+ pr_err("Invalid device node\n"); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ req_size = vma->vm_end - vma->vm_start; | |
+ fb_size = mfd->fbi->fix.smem_len; | |
+ if (req_size > fb_size) { | |
+ pr_warn("requested map is greater than framebuffer"); | |
+ return -EOVERFLOW; | |
+ } | |
+ | |
+ if (!mfd->fbi->screen_base) { | |
+ rc = mdss_fb_alloc_fb_ion_memory(mfd, fb_size); | |
+ if (rc < 0) { | |
+ pr_err("fb mmap failed!!!!"); | |
+ return rc; | |
+ } | |
+ } | |
+ | |
+ table = ion_sg_table(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ if (IS_ERR(table)) { | |
+ pr_err("Unable to get sg_table from ion:%ld\n", PTR_ERR(table)); | |
+ mfd->fbi->screen_base = NULL; | |
+ return PTR_ERR(table); | |
+ } else if (!table) { | |
+ pr_err("sg_list is NULL\n"); | |
+ mfd->fbi->screen_base = NULL; | |
+ return -EINVAL; | |
+ } | |
+ | |
+ page = sg_page(table->sgl); | |
+ if (page) { | |
+ for_each_sg(table->sgl, sg, table->nents, i) { | |
+ unsigned long remainder = vma->vm_end - addr; | |
+ unsigned long len = sg->length; | |
+ | |
+ page = sg_page(sg); | |
+ | |
+ if (offset >= sg->length) { | |
+ offset -= sg->length; | |
+ continue; | |
+ } else if (offset) { | |
+ page += offset / PAGE_SIZE; | |
+ len = sg->length - offset; | |
+ offset = 0; | |
+ } | |
+ len = min(len, remainder); | |
+ | |
+ if (mfd->mdp_fb_page_protection == | |
+ MDP_FB_PAGE_PROTECTION_WRITECOMBINE) | |
+ vma->vm_page_prot = | |
+ pgprot_writecombine(vma->vm_page_prot); | |
+ | |
+ pr_debug("vma=%p, addr=%x len=%ld", | |
+ vma, (unsigned int)addr, len); | |
+ pr_cont("vm_start=%x vm_end=%x vm_page_prot=%ld\n", | |
+ (unsigned int)vma->vm_start, | |
+ (unsigned int)vma->vm_end, | |
+ (unsigned long int)vma->vm_page_prot); | |
+ | |
+ io_remap_pfn_range(vma, addr, page_to_pfn(page), len, | |
+ vma->vm_page_prot); | |
+ addr += len; | |
+ if (addr >= vma->vm_end) | |
+ break; | |
+ } | |
+ } else { | |
+ pr_err("PAGE is null\n"); | |
+ mdss_fb_free_fb_ion_memory(mfd); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ return rc; | |
+} | |
+ | |
/* | |
- * Custom Framebuffer mmap() function for MSM driver. | |
- * Differs from standard mmap() function by allowing for customized | |
- * page-protection. | |
+ * mdss_fb_physical_mmap() - Custom fb mmap() function for MSM driver. | |
+ * | |
+ * @info - Framebuffer info. | |
+ * @vma - VM area which is part of the process virtual memory. | |
+ * | |
+ * This framebuffer mmap function differs from standard mmap() function as | |
+ * map to framebuffer memory from the CMA memory which is allocated during | |
+ * bootup. | |
+ * | |
+ * Return: virtual address is returned through vma | |
*/ | |
-static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | |
+static int mdss_fb_physical_mmap(struct fb_info *info, | |
+ struct vm_area_struct *vma) | |
{ | |
/* Get frame buffer memory range. */ | |
unsigned long start = info->fix.smem_start; | |
u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); | |
unsigned long off = vma->vm_pgoff << PAGE_SHIFT; | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
- int ret = 0; | |
if (!start) { | |
- pr_warn("No framebuffer memory is allocated.\n"); | |
+ pr_warn("No framebuffer memory is allocated\n"); | |
return -ENOMEM; | |
} | |
- ret = mdss_fb_pan_idle(mfd); | |
- if (ret) { | |
- pr_err("Shutdown pending. Aborting operation\n"); | |
- return ret; | |
- } | |
- | |
/* Set VM flags. */ | |
start &= PAGE_MASK; | |
if ((vma->vm_end <= vma->vm_start) || | |
- (off >= len) || | |
- ((vma->vm_end - vma->vm_start) > (len - off))) | |
+ (off >= len) || | |
+ ((vma->vm_end - vma->vm_start) > (len - off))) | |
return -EINVAL; | |
off += start; | |
if (off < start) | |
return -EINVAL; | |
vma->vm_pgoff = off >> PAGE_SHIFT; | |
/* This is an IO map - tell maydump to skip this VMA */ | |
- vma->vm_flags |= VM_IO | VM_RESERVED; | |
+ vma->vm_flags |= VM_IO; | |
- /* Set VM page protection */ | |
if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE) | |
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | |
- else if (mfd->mdp_fb_page_protection == | |
- MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE) | |
- vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot); | |
- else if (mfd->mdp_fb_page_protection == | |
- MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE) | |
- vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot); | |
- else if (mfd->mdp_fb_page_protection == | |
- MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE) | |
- vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot); | |
- else | |
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | |
/* Remap the frame buffer I/O range */ | |
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | |
- vma->vm_end - vma->vm_start, | |
- vma->vm_page_prot)) | |
+ vma->vm_end - vma->vm_start, | |
+ vma->vm_page_prot)) | |
return -EAGAIN; | |
return 0; | |
} | |
+static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | |
+{ | |
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
+ int rc = 0; | |
+ | |
+ if (!info->fix.smem_start && !mfd->fb_ion_handle) | |
+ rc = mdss_fb_fbmem_ion_mmap(info, vma); | |
+ else | |
+ rc = mdss_fb_physical_mmap(info, vma); | |
+ | |
+ if (rc < 0) | |
+ pr_err("fb mmap failed with rc = %d", rc); | |
+ | |
+ return rc; | |
+} | |
+ | |
static struct fb_ops mdss_fb_ops = { | |
.owner = THIS_MODULE, | |
.fb_open = mdss_fb_open, | |
@@ -946,56 +1348,70 @@ static struct fb_ops mdss_fb_ops = { | |
.fb_blank = mdss_fb_blank, /* blank display */ | |
.fb_pan_display = mdss_fb_pan_display, /* pan display */ | |
.fb_ioctl = mdss_fb_ioctl, /* perform fb specific ioctl */ | |
+#ifdef CONFIG_COMPAT | |
+ .fb_compat_ioctl = mdss_fb_compat_ioctl, | |
+#endif | |
.fb_mmap = mdss_fb_mmap, | |
}; | |
static int mdss_fb_alloc_fbmem_iommu(struct msm_fb_data_type *mfd, int dom) | |
{ | |
void *virt = NULL; | |
- unsigned long phys = 0; | |
+ phys_addr_t phys = 0; | |
size_t size = 0; | |
struct platform_device *pdev = mfd->pdev; | |
+ int rc = 0; | |
+ struct device_node *fbmem_pnode = NULL; | |
if (!pdev || !pdev->dev.of_node) { | |
pr_err("Invalid device node\n"); | |
return -ENODEV; | |
} | |
- if (of_property_read_u32(pdev->dev.of_node, | |
- "qcom,memory-reservation-size", | |
- &size) || !size) { | |
+ fbmem_pnode = of_parse_phandle(pdev->dev.of_node, | |
+ "linux,contiguous-region", 0); | |
+ if (!fbmem_pnode) { | |
+ pr_debug("fbmem is not reserved for %s\n", pdev->name); | |
mfd->fbi->screen_base = NULL; | |
mfd->fbi->fix.smem_start = 0; | |
- mfd->fbi->fix.smem_len = 0; | |
return 0; | |
+ } else { | |
+ const u32 *addr; | |
+ u64 len; | |
+ | |
+ addr = of_get_address(fbmem_pnode, 0, &len, NULL); | |
+ if (!addr) { | |
+ pr_err("fbmem size is not specified\n"); | |
+ of_node_put(fbmem_pnode); | |
+ return -EINVAL; | |
+ } | |
+ size = (size_t)len; | |
+ of_node_put(fbmem_pnode); | |
} | |
- pr_info("%s frame buffer reserve_size=0x%x\n", __func__, size); | |
+ pr_debug("%s frame buffer reserve_size=0x%zx\n", __func__, size); | |
if (size < PAGE_ALIGN(mfd->fbi->fix.line_length * | |
mfd->fbi->var.yres_virtual)) | |
pr_warn("reserve size is smaller than framebuffer size\n"); | |
- virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0); | |
+ virt = dma_alloc_coherent(&pdev->dev, size, &phys, GFP_KERNEL); | |
if (!virt) { | |
- pr_err("unable to alloc fbmem size=%u\n", size); | |
+ pr_err("unable to alloc fbmem size=%zx\n", size); | |
return -ENOMEM; | |
} | |
- phys = memory_pool_node_paddr(virt); | |
- | |
- msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0, | |
+ rc = msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0, | |
&mfd->iova); | |
- pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n", | |
- size, virt, phys, mfd->index); | |
+ if (rc) | |
+ pr_warn("Cannot map fb_mem %pa to IOMMU. rc=%d\n", &phys, rc); | |
+ | |
+ pr_debug("alloc 0x%zxB @ (%pa phys) (0x%p virt) (%pa iova) for fb%d\n", | |
+ size, &phys, virt, &mfd->iova, mfd->index); | |
mfd->fbi->screen_base = virt; | |
mfd->fbi->fix.smem_start = phys; | |
mfd->fbi->fix.smem_len = size; | |
-#ifdef CONFIG_MACH_LGE | |
- msm_fb_phys_addr_backup = phys; | |
- memset(virt,0,size); | |
-#endif | |
return 0; | |
} | |
@@ -1003,9 +1419,9 @@ static int mdss_fb_alloc_fbmem_iommu(struct msm_fb_data_type *mfd, int dom) | |
static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd) | |
{ | |
- if (mfd->mdp.fb_mem_alloc_fnc) | |
+ if (mfd->mdp.fb_mem_alloc_fnc) { | |
return mfd->mdp.fb_mem_alloc_fnc(mfd); | |
- else if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ } else if (mfd->mdp.fb_mem_get_iommu_domain) { | |
int dom = mfd->mdp.fb_mem_get_iommu_domain(); | |
if (dom >= 0) | |
return mdss_fb_alloc_fbmem_iommu(mfd, dom); | |
@@ -1183,8 +1599,14 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
var->hsync_len = panel_info->lcdc.h_pulse_width; | |
var->pixclock = panel_info->clk_rate / 1000; | |
- /* id field for fb app */ | |
+ /* | |
+ * Populate smem length here for uspace to get the | |
+ * Framebuffer size when FBIO_FSCREENINFO ioctl is | |
+ * called. | |
+ */ | |
+ fix->smem_len = PAGE_ALIGN(fix->line_length * var->yres) * mfd->fb_page; | |
+ /* id field for fb app */ | |
id = (int *)&mfd->panel; | |
snprintf(fix->id, sizeof(fix->id), "mdssfb_%x", (u32) *id); | |
@@ -1197,12 +1619,10 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
mfd->panel_power_on = false; | |
mfd->dcm_state = DCM_UNINIT; | |
- mdss_fb_parse_dt_split(mfd); | |
+ mdss_fb_parse_dt(mfd); | |
- if (mdss_fb_alloc_fbmem(mfd)) { | |
- pr_err("unable to allocate framebuffer memory\n"); | |
- return -ENOMEM; | |
- } | |
+ if (mdss_fb_alloc_fbmem(mfd)) | |
+ pr_warn("unable to allocate fb memory in fb register\n"); | |
mfd->op_enable = true; | |
@@ -1211,16 +1631,22 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
mutex_init(&mfd->mdp_sync_pt_data.sync_mutex); | |
atomic_set(&mfd->mdp_sync_pt_data.commit_cnt, 0); | |
atomic_set(&mfd->commits_pending, 0); | |
+ atomic_set(&mfd->ioctl_ref_cnt, 0); | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
init_timer(&mfd->no_update.timer); | |
mfd->no_update.timer.function = mdss_fb_no_update_notify_timer_cb; | |
mfd->no_update.timer.data = (unsigned long)mfd; | |
+ mfd->update.ref_count = 0; | |
+ mfd->no_update.ref_count = 0; | |
init_completion(&mfd->update.comp); | |
init_completion(&mfd->no_update.comp); | |
init_completion(&mfd->power_off_comp); | |
init_completion(&mfd->power_set_comp); | |
init_waitqueue_head(&mfd->commit_wait_q); | |
init_waitqueue_head(&mfd->idle_wait_q); | |
+ init_waitqueue_head(&mfd->ioctl_q); | |
+ init_waitqueue_head(&mfd->kickoff_wait_q); | |
ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | |
if (ret) | |
@@ -1233,9 +1659,8 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
return -EPERM; | |
} | |
- pr_info("FrameBuffer[%d] %dx%d size=%d registered successfully!\n", | |
- mfd->index, fbi->var.xres, fbi->var.yres, | |
- fbi->fix.smem_len); | |
+ pr_info("FrameBuffer[%d] %dx%d registered successfully!\n", mfd->index, | |
+ fbi->var.xres, fbi->var.yres); | |
return 0; | |
} | |
@@ -1246,9 +1671,11 @@ static int mdss_fb_open(struct fb_info *info, int user) | |
struct mdss_fb_proc_info *pinfo = NULL; | |
int result; | |
int pid = current->tgid; | |
+ struct task_struct *task = current->group_leader; | |
if (mfd->shutdown_pending) { | |
- pr_err("Shutdown pending. Aborting operation\n"); | |
+ pr_err("Shutdown pending. Aborting operation. Request from pid:%d name=%s\n", | |
+ pid, task->comm); | |
return -EPERM; | |
} | |
@@ -1331,6 +1758,12 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) | |
return -EINVAL; | |
} | |
+ if (!wait_event_timeout(mfd->ioctl_q, | |
+ !atomic_read(&mfd->ioctl_ref_cnt) || !release_all, | |
+ msecs_to_jiffies(1000))) | |
+ pr_warn("fb%d ioctl could not finish. waited 1 sec.\n", | |
+ mfd->index); | |
+ | |
mdss_fb_pan_idle(mfd); | |
pr_debug("release_all = %s\n", release_all ? "true" : "false"); | |
@@ -1401,6 +1834,16 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) | |
mfd->disp_thread = NULL; | |
} | |
+ if (mfd->mdp.release_fnc) { | |
+ ret = mfd->mdp.release_fnc(mfd, true); | |
+ if (ret) | |
+ pr_err("error fb%d release process %s pid=%d\n", | |
+ mfd->index, task->comm, pid); | |
+ } | |
+ | |
+ if (mfd->fb_ion_handle) | |
+ mdss_fb_free_fb_ion_memory(mfd); | |
+ | |
ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, | |
mfd->op_enable); | |
if (ret) { | |
@@ -1408,6 +1851,7 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) | |
mfd->index, ret, task->comm, pid); | |
return ret; | |
} | |
+ atomic_set(&mfd->ioctl_ref_cnt, 0); | |
} | |
return ret; | |
@@ -1608,6 +2052,25 @@ static int mdss_fb_pan_idle(struct msm_fb_data_type *mfd) | |
return 0; | |
} | |
+static int mdss_fb_wait_for_kickoff(struct msm_fb_data_type *mfd) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = wait_event_timeout(mfd->kickoff_wait_q, | |
+ (!atomic_read(&mfd->kickoff_pending) || | |
+ mfd->shutdown_pending), | |
+ msecs_to_jiffies(WAIT_DISP_OP_TIMEOUT / 2)); | |
+ if (!ret) { | |
+ pr_err("wait for kickoff timeout %d pending=%d\n", | |
+ ret, atomic_read(&mfd->kickoff_pending)); | |
+ | |
+ } else if (mfd->shutdown_pending) { | |
+ pr_debug("Shutdown signalled\n"); | |
+ return -EPERM; | |
+ } | |
+ | |
+ return 0; | |
+} | |
static int mdss_fb_pan_display_ex(struct fb_info *info, | |
struct mdp_display_commit *disp_commit) | |
@@ -1617,7 +2080,11 @@ static int mdss_fb_pan_display_ex(struct fb_info *info, | |
u32 wait_for_finish = disp_commit->wait_for_finish; | |
int ret = 0; | |
- if (!mfd || (!mfd->op_enable) || (!mfd->panel_power_on)) | |
+ if (!mfd || (!mfd->op_enable)) | |
+ return -EPERM; | |
+ | |
+ if ((!mfd->panel_power_on) && !((mfd->dcm_state == DCM_ENTER) && | |
+ (mfd->panel.type == MIPI_CMD_PANEL))) | |
return -EPERM; | |
if (var->xoffset > (info->var.xres_virtual - info->var.xres)) | |
@@ -1646,6 +2113,7 @@ static int mdss_fb_pan_display_ex(struct fb_info *info, | |
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt); | |
atomic_inc(&mfd->commits_pending); | |
+ atomic_inc(&mfd->kickoff_pending); | |
wake_up_all(&mfd->commit_wait_q); | |
mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex); | |
if (wait_for_finish) | |
@@ -1668,7 +2136,11 @@ static int mdss_fb_pan_display_sub(struct fb_var_screeninfo *var, | |
{ | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
- if ((!mfd->op_enable) || (!mfd->panel_power_on)) | |
+ if (!mfd->op_enable) | |
+ return -EPERM; | |
+ | |
+ if ((!mfd->panel_power_on) && !((mfd->dcm_state == DCM_ENTER) && | |
+ (mfd->panel.type == MIPI_CMD_PANEL))) | |
return -EPERM; | |
if (var->xoffset > (info->var.xres_virtual - info->var.xres)) | |
@@ -1738,6 +2210,8 @@ static int __mdss_fb_perform_commit(struct msm_fb_data_type *mfd) | |
if (ret) | |
pr_err("pan display failed %x on fb%d\n", ret, | |
mfd->index); | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
+ wake_up_all(&mfd->kickoff_wait_q); | |
} | |
if (!ret) | |
mdss_fb_update_backlight(mfd); | |
@@ -1774,6 +2248,7 @@ static int __mdss_fb_display_thread(void *data) | |
} | |
atomic_set(&mfd->commits_pending, 0); | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
wake_up_all(&mfd->idle_wait_q); | |
return ret; | |
@@ -1944,6 +2419,8 @@ static int mdss_fb_set_par(struct fb_info *info) | |
else | |
mfd->fbi->fix.line_length = var->xres * var->bits_per_pixel / 8; | |
+ mfd->fbi->fix.smem_len = mfd->fbi->fix.line_length * | |
+ mfd->fbi->var.yres_virtual; | |
if (mfd->panel_reconfig || (mfd->fb_imgType != old_imgType)) { | |
mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); | |
@@ -2233,6 +2710,28 @@ static int mdss_fb_display_commit(struct fb_info *info, | |
return ret; | |
} | |
+static int __ioctl_wait_idle(struct msm_fb_data_type *mfd, u32 cmd) | |
+{ | |
+ int ret = 0; | |
+ | |
+ if (mfd->wait_for_kickoff && | |
+ ((cmd == MSMFB_OVERLAY_PREPARE) || | |
+ (cmd == MSMFB_BUFFER_SYNC) || | |
+ (cmd == MSMFB_OVERLAY_SET))) { | |
+ ret = mdss_fb_wait_for_kickoff(mfd); | |
+ } else if ((cmd != MSMFB_VSYNC_CTRL) && | |
+ (cmd != MSMFB_OVERLAY_VSYNC_CTRL) && | |
+ (cmd != MSMFB_ASYNC_BLIT) && | |
+ (cmd != MSMFB_BLIT) && | |
+ (cmd != MSMFB_NOTIFY_UPDATE) && | |
+ (cmd != MSMFB_OVERLAY_PREPARE)) { | |
+ ret = mdss_fb_pan_idle(mfd); | |
+ } | |
+ | |
+ if (ret) | |
+ pr_debug("Shutdown pending. Aborting operation %x\n", cmd); | |
+ return ret; | |
+} | |
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
unsigned long arg) | |
@@ -2243,25 +2742,25 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
int ret = -ENOSYS; | |
struct mdp_buf_sync buf_sync; | |
struct msm_sync_pt_data *sync_pt_data = NULL; | |
-#ifdef CONFIG_MACH_LGE | |
- u32 dsi_panel_invert = 0; | |
-#endif | |
+ unsigned int dsi_mode = 0; | |
if (!info || !info->par) | |
return -EINVAL; | |
+ | |
mfd = (struct msm_fb_data_type *)info->par; | |
+ if (!mfd) | |
+ return -EINVAL; | |
+ | |
+ if (mfd->shutdown_pending) | |
+ return -EPERM; | |
+ | |
+ atomic_inc(&mfd->ioctl_ref_cnt); | |
+ | |
mdss_fb_power_setting_idle(mfd); | |
- if ((cmd != MSMFB_VSYNC_CTRL) && (cmd != MSMFB_OVERLAY_VSYNC_CTRL) && | |
- (cmd != MSMFB_ASYNC_BLIT) && (cmd != MSMFB_BLIT) && | |
- (cmd != MSMFB_NOTIFY_UPDATE) && | |
- (cmd != MSMFB_OVERLAY_PREPARE)) { | |
- ret = mdss_fb_pan_idle(mfd); | |
- if (ret) { | |
- pr_debug("Shutdown pending. Aborting operation %x\n", | |
- cmd); | |
- return ret; | |
- } | |
- } | |
+ | |
+ ret = __ioctl_wait_idle(mfd, cmd); | |
+ if (ret) | |
+ goto exit; | |
switch (cmd) { | |
case MSMFB_CURSOR: | |
@@ -2278,15 +2777,19 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
ret = copy_to_user(argp, &fb_page_protection, | |
sizeof(fb_page_protection)); | |
if (ret) | |
- return ret; | |
+ goto exit; | |
break; | |
case MSMFB_BUFFER_SYNC: | |
ret = copy_from_user(&buf_sync, argp, sizeof(buf_sync)); | |
if (ret) | |
- return ret; | |
- if ((!mfd->op_enable) || (!mfd->panel_power_on)) | |
- return -EPERM; | |
+ goto exit; | |
+ | |
+ if ((!mfd->op_enable) || (!mfd->panel_power_on)) { | |
+ ret = -EPERM; | |
+ goto exit; | |
+ } | |
+ | |
if (mfd->mdp.get_sync_fnc) | |
sync_pt_data = mfd->mdp.get_sync_fnc(mfd, &buf_sync); | |
if (!sync_pt_data) | |
@@ -2306,14 +2809,15 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
ret = mdss_fb_display_commit(info, argp); | |
break; | |
-#ifdef CONFIG_MACH_LGE | |
- case MSMFB_INVERT_PANEL: | |
- ret = copy_from_user(&dsi_panel_invert, argp, sizeof(int)); | |
- if(ret) | |
- return ret; | |
- ret = mdss_dsi_panel_invert(dsi_panel_invert); | |
- break; | |
-#endif | |
+ case MSMFB_LPM_ENABLE: | |
+ ret = copy_from_user(&dsi_mode, argp, sizeof(dsi_mode)); | |
+ if (ret) { | |
+ pr_err("%s: MSMFB_LPM_ENABLE ioctl failed\n", __func__); | |
+ goto exit; | |
+ } | |
+ | |
+ ret = mdss_fb_lpm_enable(mfd, dsi_mode); | |
+ break; | |
default: | |
if (mfd->mdp.ioctl_handler) | |
@@ -2324,6 +2828,10 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
if (ret == -ENOSYS) | |
pr_err("unsupported ioctl (%x)\n", cmd); | |
+exit: | |
+ if (!atomic_dec_return(&mfd->ioctl_ref_cnt)) | |
+ wake_up_all(&mfd->ioctl_q); | |
+ | |
return ret; | |
} | |
@@ -2359,13 +2867,6 @@ static int mdss_fb_register_extra_panel(struct platform_device *pdev, | |
return -EEXIST; | |
} | |
- if ((fb_pdata->panel_info.type != MIPI_VIDEO_PANEL) || | |
- (pdata->panel_info.type != MIPI_VIDEO_PANEL)) { | |
- pr_err("Split panel not supported for panel type %d\n", | |
- pdata->panel_info.type); | |
- return -EINVAL; | |
- } | |
- | |
fb_pdata->next = pdata; | |
return 0; | |
@@ -2411,7 +2912,8 @@ int mdss_register_panel(struct platform_device *pdev, | |
pr_info("adding framebuffer device %s\n", dev_name(&pdev->dev)); | |
fb_pdev = of_platform_device_create(node, NULL, | |
&mdss_pdev->dev); | |
- fb_pdev->dev.platform_data = pdata; | |
+ if (fb_pdev) | |
+ fb_pdev->dev.platform_data = pdata; | |
} | |
if (master_panel && mdp_instance->panel_register_done) | |
@@ -2440,7 +2942,7 @@ int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num) | |
struct fb_info *info; | |
struct msm_fb_data_type *mfd; | |
- if (fb_num > MAX_FBI_LIST) | |
+ if (fb_num >= MAX_FBI_LIST) | |
return -EINVAL; | |
info = fbi_list[fb_num]; | |
@@ -2472,3 +2974,27 @@ int __init mdss_fb_init(void) | |
} | |
module_init(mdss_fb_init); | |
+ | |
+int mdss_fb_suspres_panel(struct device *dev, void *data) | |
+{ | |
+ struct msm_fb_data_type *mfd; | |
+ int rc; | |
+ u32 event; | |
+ | |
+ if (!data) { | |
+ pr_err("Device state not defined\n"); | |
+ return -EINVAL; | |
+ } | |
+ mfd = dev_get_drvdata(dev); | |
+ if (!mfd) | |
+ return 0; | |
+ | |
+ event = *((bool *) data) ? MDSS_EVENT_RESUME : MDSS_EVENT_SUSPEND; | |
+ | |
+ rc = mdss_fb_send_panel_event(mfd, event, NULL); | |
+ if (rc) | |
+ pr_warn("unable to %s fb%d (%d)\n", | |
+ event == MDSS_EVENT_RESUME ? "resume" : "suspend", | |
+ mfd->index, rc); | |
+ return rc; | |
+} | |
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h | |
index 11295d6..a9def29 100644 | |
--- a/drivers/video/msm/mdss/mdss_fb.h | |
+++ b/drivers/video/msm/mdss/mdss_fb.h | |
@@ -21,6 +21,7 @@ | |
#include <linux/notifier.h> | |
#include "mdss_panel.h" | |
+#include "mdss_mdp_splash_logo.h" | |
#define MSM_FB_DEFAULT_PAGE_SIZE 2 | |
#define MFD_KEY 0x11161126 | |
@@ -41,6 +42,9 @@ | |
#define MIN(x, y) (((x) < (y)) ? (x) : (y)) | |
#endif | |
+#define MDP_PP_AD_BL_LINEAR 0x0 | |
+#define MDP_PP_AD_BL_LINEAR_INV 0x1 | |
+ | |
/** | |
* enum mdp_notify_event - Different frame events to indicate frame update state | |
* | |
@@ -78,6 +82,8 @@ struct disp_info_notify { | |
struct completion comp; | |
struct mutex lock; | |
int value; | |
+ int is_suspend; | |
+ int ref_count; | |
}; | |
struct msm_sync_pt_data { | |
@@ -119,11 +125,15 @@ struct msm_mdp_interface { | |
int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap); | |
int (*do_histogram)(struct msm_fb_data_type *mfd, | |
struct mdp_histogram *hist); | |
- int (*update_ad_input)(struct msm_fb_data_type *mfd); | |
+ int (*ad_calc_bl)(struct msm_fb_data_type *mfd, int bl_in, | |
+ int *bl_out, bool *bl_out_notify); | |
int (*panel_register_done)(struct mdss_panel_data *pdata); | |
u32 (*fb_stride)(u32 fb_index, u32 xres, int bpp); | |
+ int (*splash_init_fnc)(struct msm_fb_data_type *mfd); | |
struct msm_sync_pt_data *(*get_sync_fnc)(struct msm_fb_data_type *mfd, | |
const struct mdp_buf_sync *buf_sync); | |
+ void (*check_dsi_status)(struct work_struct *work, uint32_t interval); | |
+ int (*configure_panel)(struct msm_fb_data_type *mfd, int mode); | |
void *private1; | |
}; | |
@@ -179,14 +189,15 @@ struct msm_fb_data_type { | |
int ext_ad_ctrl; | |
u32 ext_bl_ctrl; | |
u32 calib_mode; | |
+ u32 ad_bl_level; | |
u32 bl_level; | |
u32 bl_scale; | |
u32 bl_min_lvl; | |
u32 unset_bl_level; | |
u32 bl_updated; | |
- u32 bl_level_old; | |
+ u32 bl_level_scaled; | |
+ u32 bl_level_prev_scaled; | |
struct mutex bl_lock; | |
- struct mutex lock; | |
struct platform_device *pdev; | |
@@ -203,16 +214,26 @@ struct msm_fb_data_type { | |
/* for non-blocking */ | |
struct task_struct *disp_thread; | |
atomic_t commits_pending; | |
+ atomic_t kickoff_pending; | |
wait_queue_head_t commit_wait_q; | |
wait_queue_head_t idle_wait_q; | |
+ wait_queue_head_t kickoff_wait_q; | |
bool shutdown_pending; | |
+ struct msm_fb_splash_info splash_info; | |
+ | |
+ wait_queue_head_t ioctl_q; | |
+ atomic_t ioctl_ref_cnt; | |
+ | |
struct msm_fb_backup_type msm_fb_backup; | |
struct completion power_set_comp; | |
u32 is_power_setting; | |
u32 dcm_state; | |
struct list_head proc_list; | |
+ u32 wait_for_kickoff; | |
+ struct ion_client *fb_ion_client; | |
+ struct ion_handle *fb_ion_handle; | |
}; | |
static inline void mdss_fb_update_notify_update(struct msm_fb_data_type *mfd) | |
@@ -243,4 +264,5 @@ struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline, | |
const char *fence_name, int val); | |
int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp); | |
int mdss_fb_dcm(struct msm_fb_data_type *mfd, int req_state); | |
+int mdss_fb_suspres_panel(struct device *dev, void *data); | |
#endif /* MDSS_FB_H */ | |
diff --git a/drivers/video/msm/mdss/mdss_hdmi_edid.c b/drivers/video/msm/mdss/mdss_hdmi_edid.c | |
index a45876b..d0b89a3 100644 | |
--- a/drivers/video/msm/mdss/mdss_hdmi_edid.c | |
+++ b/drivers/video/msm/mdss/mdss_hdmi_edid.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2010-2014, 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 | |
@@ -1120,7 +1120,7 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl, | |
u8 i = 0, offset = 0, std_blk = 0; | |
u32 video_format = HDMI_VFRMT_640x480p60_4_3; | |
u32 has480p = false; | |
- u8 len; | |
+ u8 len = 0; | |
int rc; | |
const u8 *edid_blk0 = NULL; | |
const u8 *edid_blk1 = NULL; | |
@@ -1140,7 +1140,7 @@ static void hdmi_edid_get_display_mode(struct hdmi_edid_ctrl *edid_ctrl, | |
hdmi_edid_find_block(data_buf+0x80, DBC_START_OFFSET, | |
VIDEO_DATA_BLOCK, &len) : NULL; | |
- if (svd == NULL || len == 0 || len > MAX_DATA_BLOCK_SIZE) { | |
+ if (num_of_cea_blocks && (len == 0 || len > MAX_DATA_BLOCK_SIZE)) { | |
DEV_DBG("%s: No/Invalid Video Data Block\n", | |
__func__); | |
return; | |
diff --git a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c | |
index e56e9fa..2240941 100644 | |
--- a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c | |
+++ b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c | |
@@ -1050,7 +1050,10 @@ static int hdmi_msm_if_abort_reauth(struct hdmi_hdcp_ctrl *hdcp_ctrl) | |
} | |
if (++hdcp_ctrl->auth_retries == AUTH_RETRIES_TIME) { | |
- hdmi_hdcp_off(hdcp_ctrl); | |
+ mutex_lock(hdcp_ctrl->init_data.mutex); | |
+ hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; | |
+ mutex_unlock(hdcp_ctrl->init_data.mutex); | |
+ | |
hdcp_ctrl->auth_retries = 0; | |
ret = -ERANGE; | |
} | |
@@ -1077,13 +1080,6 @@ int hdmi_hdcp_reauthenticate(void *input) | |
return 0; | |
} | |
- ret = hdmi_msm_if_abort_reauth(hdcp_ctrl); | |
- | |
- if (ret) { | |
- DEV_ERR("%s: abort reauthentication!\n", __func__); | |
- return ret; | |
- } | |
- | |
/* | |
* Disable HPD circuitry. | |
* This is needed to reset the HDCP cipher engine so that when we | |
@@ -1109,6 +1105,13 @@ int hdmi_hdcp_reauthenticate(void *input) | |
DSS_REG_R(hdcp_ctrl->init_data.core_io, | |
HDMI_HPD_CTRL) | BIT(28)); | |
+ ret = hdmi_msm_if_abort_reauth(hdcp_ctrl); | |
+ | |
+ if (ret) { | |
+ DEV_ERR("%s: abort reauthentication!\n", __func__); | |
+ return ret; | |
+ } | |
+ | |
/* Restart authentication attempt */ | |
DEV_DBG("%s: %s: Scheduling work to start HDCP authentication", | |
__func__, HDCP_STATE_NAME); | |
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c | |
index a29fb751..1bf389a 100644 | |
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c | |
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2010-2014, 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 | |
@@ -20,6 +20,7 @@ | |
#include <linux/of_gpio.h> | |
#include <linux/of_platform.h> | |
#include <linux/types.h> | |
+#include <linux/msm_hdmi.h> | |
#include <mach/msm_hdmi_audio_codec.h> | |
#define REG_DUMP 0 | |
@@ -119,6 +120,7 @@ static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl, | |
static inline void hdmi_tx_set_audio_switch_node(struct hdmi_tx_ctrl *hdmi_ctrl, | |
int val, bool force); | |
static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl); | |
+static void hdmi_tx_en_encryption(struct hdmi_tx_ctrl *hdmi_ctrl, u32 on); | |
struct mdss_hw hdmi_tx_hw = { | |
.hw_ndx = MDSS_HW_HDMI, | |
@@ -133,6 +135,7 @@ struct dss_gpio hpd_gpio_config[] = { | |
}; | |
struct dss_gpio ddc_gpio_config[] = { | |
+ {0, 1, COMPATIBLE_NAME "-ddc-mux-sel"}, | |
{0, 1, COMPATIBLE_NAME "-ddc-clk"}, | |
{0, 1, COMPATIBLE_NAME "-ddc-data"} | |
}; | |
@@ -222,6 +225,77 @@ static const struct hdmi_tx_audio_acr_arry hdmi_tx_audio_acr_lut[] = { | |
{20480, 247500} } }, | |
}; | |
+int register_hdmi_cable_notification(struct hdmi_cable_notify *handler) | |
+{ | |
+ struct hdmi_tx_ctrl *hdmi_ctrl = NULL; | |
+ struct list_head *pos; | |
+ | |
+ if (!hdmi_tx_hw.ptr) { | |
+ DEV_WARN("%s: HDMI Tx core not ready\n", __func__); | |
+ return -EPROBE_DEFER; | |
+ } | |
+ | |
+ if (!handler) { | |
+ DEV_ERR("%s: Empty handler\n", __func__); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ hdmi_ctrl = (struct hdmi_tx_ctrl *) hdmi_tx_hw.ptr; | |
+ | |
+ mutex_lock(&hdmi_ctrl->cable_notify_mutex); | |
+ handler->status = hdmi_ctrl->hpd_state; | |
+ list_for_each(pos, &hdmi_ctrl->cable_notify_handlers); | |
+ list_add_tail(&handler->link, pos); | |
+ mutex_unlock(&hdmi_ctrl->cable_notify_mutex); | |
+ | |
+ return handler->status; | |
+} /* register_hdmi_cable_notification */ | |
+ | |
+int unregister_hdmi_cable_notification(struct hdmi_cable_notify *handler) | |
+{ | |
+ struct hdmi_tx_ctrl *hdmi_ctrl = NULL; | |
+ | |
+ if (!hdmi_tx_hw.ptr) { | |
+ DEV_WARN("%s: HDMI Tx core not ready\n", __func__); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ if (!handler) { | |
+ DEV_ERR("%s: Empty handler\n", __func__); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ hdmi_ctrl = (struct hdmi_tx_ctrl *) hdmi_tx_hw.ptr; | |
+ | |
+ mutex_lock(&hdmi_ctrl->cable_notify_mutex); | |
+ list_del(&handler->link); | |
+ mutex_unlock(&hdmi_ctrl->cable_notify_mutex); | |
+ | |
+ return 0; | |
+} /* unregister_hdmi_cable_notification */ | |
+ | |
+static void hdmi_tx_cable_notify_work(struct work_struct *work) | |
+{ | |
+ struct hdmi_tx_ctrl *hdmi_ctrl = NULL; | |
+ struct hdmi_cable_notify *pos; | |
+ | |
+ hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, cable_notify_work); | |
+ | |
+ if (!hdmi_ctrl) { | |
+ DEV_ERR("%s: invalid hdmi data\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ mutex_lock(&hdmi_ctrl->cable_notify_mutex); | |
+ list_for_each_entry(pos, &hdmi_ctrl->cable_notify_handlers, link) { | |
+ if (pos->status != hdmi_ctrl->hpd_state) { | |
+ pos->status = hdmi_ctrl->hpd_state; | |
+ pos->hpd_notify(pos); | |
+ } | |
+ } | |
+ mutex_unlock(&hdmi_ctrl->cable_notify_mutex); | |
+} /* hdmi_tx_cable_notify_work */ | |
+ | |
static bool hdmi_tx_is_cea_format(int mode) | |
{ | |
bool cea_fmt; | |
@@ -327,6 +401,9 @@ static inline void hdmi_tx_send_cable_notification( | |
if (!hdmi_ctrl->pdata.primary && (hdmi_ctrl->sdev.state != val)) | |
switch_set_state(&hdmi_ctrl->sdev, val); | |
+ | |
+ /* Notify all registered modules of cable connection status */ | |
+ schedule_work(&hdmi_ctrl->cable_notify_work); | |
} /* hdmi_tx_send_cable_notification */ | |
static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) | |
@@ -840,11 +917,11 @@ void hdmi_tx_hdcp_cb(void *ptr, enum hdmi_hdcp_state status) | |
switch (status) { | |
case HDCP_STATE_AUTHENTICATED: | |
if (hdmi_ctrl->hpd_state) { | |
- /* Clear AV Mute */ | |
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 0); | |
- if (rc) | |
- DEV_ERR("%s: Failed to clear av mute. rc=%d\n", | |
- __func__, rc); | |
+ if (hdmi_ctrl->pdata.primary) | |
+ hdmi_tx_en_encryption(hdmi_ctrl, true); | |
+ else | |
+ /* Clear AV Mute */ | |
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 0); | |
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 1, false); | |
} | |
break; | |
@@ -852,11 +929,11 @@ void hdmi_tx_hdcp_cb(void *ptr, enum hdmi_hdcp_state status) | |
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false); | |
if (hdmi_ctrl->hpd_state) { | |
- /* Set AV Mute */ | |
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 1); | |
- if (rc) | |
- DEV_ERR("%s: Failed to set av mute. rc=%d\n", | |
- __func__, rc); | |
+ if (hdmi_ctrl->pdata.primary) | |
+ hdmi_tx_en_encryption(hdmi_ctrl, false); | |
+ else | |
+ /* Set AV Mute */ | |
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1); | |
DEV_DBG("%s: Reauthenticating\n", __func__); | |
rc = hdmi_hdcp_reauthenticate( | |
@@ -1050,6 +1127,17 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) | |
DEV_DBG("%s: Got HPD interrupt\n", __func__); | |
if (hdmi_ctrl->hpd_state) { | |
+ /* | |
+ * If a down stream device or bridge chip is attached to hdmi | |
+ * Tx core output, it is likely that it might be powering the | |
+ * hpd module ON/OFF on cable connect/disconnect as it would | |
+ * have its own mechanism of detecting cable. Flush power off | |
+ * work is needed in case there is any race condidtion between | |
+ * power off and on during fast cable plug in/out. | |
+ */ | |
+ if (hdmi_ctrl->ds_registered) | |
+ flush_work(&hdmi_ctrl->power_off_work); | |
+ | |
if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) { | |
DEV_ERR("%s: Failed to enable ddc power\n", __func__); | |
return; | |
@@ -1066,6 +1154,13 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) | |
hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false); | |
hdmi_tx_wait_for_audio_engine(hdmi_ctrl); | |
+ if (!hdmi_ctrl->panel_power_on) { | |
+ if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, | |
+ false)) | |
+ DEV_WARN("%s: Failed to disable ddc power\n", | |
+ __func__); | |
+ } | |
+ | |
hdmi_tx_send_cable_notification(hdmi_ctrl, 0); | |
DEV_INFO("%s: sense cable DISCONNECTED: state switch to %d\n", | |
__func__, hdmi_ctrl->sdev.state); | |
@@ -1570,6 +1665,28 @@ static void hdmi_tx_set_spd_infoframe(struct hdmi_tx_ctrl *hdmi_ctrl) | |
DSS_REG_W(io, HDMI_GEN_PKT_CTRL, packet_control); | |
} /* hdmi_tx_set_spd_infoframe */ | |
+static void hdmi_tx_en_encryption(struct hdmi_tx_ctrl *hdmi_ctrl, u32 on) | |
+{ | |
+ u32 reg_val; | |
+ struct dss_io_data *io = NULL; | |
+ | |
+ if (!hdmi_ctrl->hdcp_feature_on || !hdmi_ctrl->present_hdcp) | |
+ return; | |
+ | |
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; | |
+ | |
+ mutex_lock(&hdmi_ctrl->mutex); | |
+ reg_val = DSS_REG_R_ND(io, HDMI_CTRL); | |
+ | |
+ if (on) | |
+ reg_val |= BIT(2); | |
+ else | |
+ reg_val &= ~BIT(2); | |
+ DSS_REG_W(io, HDMI_CTRL, reg_val); | |
+ | |
+ mutex_unlock(&hdmi_ctrl->mutex); | |
+} /* hdmi_tx_en_encryption */ | |
+ | |
static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) | |
{ | |
struct dss_io_data *io = NULL; | |
@@ -1586,12 +1703,14 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) | |
return; | |
} | |
+ mutex_lock(&hdmi_ctrl->mutex); | |
if (power_on) { | |
/* Enable the block */ | |
reg_val |= BIT(0); | |
/* HDMI Encryption, if HDCP is enabled */ | |
- if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) | |
+ if (hdmi_ctrl->hdcp_feature_on && | |
+ hdmi_ctrl->present_hdcp && !hdmi_ctrl->pdata.primary) | |
reg_val |= BIT(2); | |
/* Set transmission mode to DVI based in EDID info */ | |
@@ -1601,6 +1720,7 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) | |
} | |
DSS_REG_W(io, HDMI_CTRL, reg_val); | |
+ mutex_unlock(&hdmi_ctrl->mutex); | |
DEV_DBG("HDMI Core: %s, HDMI_CTRL=0x%08x\n", | |
power_on ? "Enable" : "Disable", reg_val); | |
@@ -2242,6 +2362,8 @@ int msm_hdmi_register_mhl(struct platform_device *pdev, | |
ops->set_mhl_max_pclk = hdmi_tx_set_mhl_max_pclk; | |
ops->set_upstream_hpd = hdmi_tx_set_mhl_hpd; | |
+ hdmi_ctrl->ds_registered = true; | |
+ | |
return 0; | |
} | |
@@ -2566,6 +2688,11 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) | |
hdmi_ctrl->panel_power_on = true; | |
mutex_unlock(&hdmi_ctrl->mutex); | |
+ if (hdmi_ctrl->pdata.primary) { | |
+ if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) | |
+ DEV_ERR("%s: Failed to enable ddc power\n", __func__); | |
+ } | |
+ | |
hdmi_cec_config(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]); | |
if (hdmi_ctrl->hpd_state) { | |
@@ -2835,6 +2962,7 @@ static void hdmi_tx_dev_deinit(struct hdmi_tx_ctrl *hdmi_ctrl) | |
if (hdmi_ctrl->workq) | |
destroy_workqueue(hdmi_ctrl->workq); | |
mutex_destroy(&hdmi_ctrl->lut_lock); | |
+ mutex_destroy(&hdmi_ctrl->cable_notify_mutex); | |
mutex_destroy(&hdmi_ctrl->mutex); | |
hdmi_tx_hw.ptr = NULL; | |
@@ -2864,6 +2992,10 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) | |
hdmi_setup_video_mode_lut(); | |
mutex_init(&hdmi_ctrl->mutex); | |
mutex_init(&hdmi_ctrl->lut_lock); | |
+ mutex_init(&hdmi_ctrl->cable_notify_mutex); | |
+ | |
+ INIT_LIST_HEAD(&hdmi_ctrl->cable_notify_handlers); | |
+ | |
hdmi_ctrl->workq = create_workqueue("hdmi_tx_workq"); | |
if (!hdmi_ctrl->workq) { | |
DEV_ERR("%s: hdmi_tx_workq creation failed.\n", __func__); | |
@@ -2881,8 +3013,9 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) | |
hdmi_ctrl->hpd_initialized = false; | |
hdmi_ctrl->hpd_off_pending = false; | |
init_completion(&hdmi_ctrl->hpd_done); | |
- INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work); | |
+ INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work); | |
+ INIT_WORK(&hdmi_ctrl->cable_notify_work, hdmi_tx_cable_notify_work); | |
INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work); | |
spin_lock_init(&hdmi_ctrl->hpd_state_lock); | |
@@ -3027,10 +3160,10 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, | |
case MDSS_EVENT_PANEL_ON: | |
if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) { | |
/* Set AV Mute before starting authentication */ | |
- rc = hdmi_tx_config_avmute(hdmi_ctrl, 1); | |
- if (rc) | |
- DEV_ERR("%s: Failed to set av mute. rc=%d\n", | |
- __func__, rc); | |
+ if (hdmi_ctrl->pdata.primary) | |
+ hdmi_tx_en_encryption(hdmi_ctrl, false); | |
+ else | |
+ rc = hdmi_tx_config_avmute(hdmi_ctrl, 1); | |
DEV_DBG("%s: Starting HDCP authentication\n", __func__); | |
rc = hdmi_hdcp_authenticate( | |
@@ -3207,7 +3340,7 @@ static int hdmi_tx_get_dt_clk_data(struct device *dev, | |
switch (module_type) { | |
case HDMI_TX_HPD_PM: | |
- mp->num_clk = 3; | |
+ mp->num_clk = 4; | |
mp->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * | |
mp->num_clk, GFP_KERNEL); | |
if (!mp->clk_config) { | |
@@ -3233,10 +3366,14 @@ static int hdmi_tx_get_dt_clk_data(struct device *dev, | |
snprintf(mp->clk_config[2].clk_name, 32, "%s", "mdp_core_clk"); | |
mp->clk_config[2].type = DSS_CLK_AHB; | |
mp->clk_config[2].rate = 0; | |
+ | |
+ snprintf(mp->clk_config[3].clk_name, 32, "%s", "alt_iface_clk"); | |
+ mp->clk_config[3].type = DSS_CLK_AHB; | |
+ mp->clk_config[3].rate = 0; | |
break; | |
case HDMI_TX_CORE_PM: | |
- mp->num_clk = 2; | |
+ mp->num_clk = 1; | |
mp->clk_config = devm_kzalloc(dev, sizeof(struct dss_clk) * | |
mp->num_clk, GFP_KERNEL); | |
if (!mp->clk_config) { | |
@@ -3249,10 +3386,6 @@ static int hdmi_tx_get_dt_clk_data(struct device *dev, | |
mp->clk_config[0].type = DSS_CLK_PCLK; | |
/* This rate will be overwritten when core is powered on */ | |
mp->clk_config[0].rate = 148500000; | |
- | |
- snprintf(mp->clk_config[1].clk_name, 32, "%s", "alt_iface_clk"); | |
- mp->clk_config[1].type = DSS_CLK_AHB; | |
- mp->clk_config[1].rate = 0; | |
break; | |
case HDMI_TX_DDC_PM: | |
@@ -3759,6 +3892,7 @@ static int __devexit hdmi_tx_remove(struct platform_device *pdev) | |
static const struct of_device_id hdmi_tx_dt_match[] = { | |
{.compatible = COMPATIBLE_NAME,}, | |
+ { /* Sentinel */ }, | |
}; | |
MODULE_DEVICE_TABLE(of, hdmi_tx_dt_match); | |
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h | |
index 0787dee..54d80dc 100644 | |
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h | |
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2010-2014, 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 | |
@@ -55,6 +55,8 @@ struct hdmi_tx_ctrl { | |
struct mutex mutex; | |
struct mutex lut_lock; | |
+ struct mutex cable_notify_mutex; | |
+ struct list_head cable_notify_handlers; | |
struct kobject *kobj; | |
struct switch_dev sdev; | |
struct switch_dev audio_sdev; | |
@@ -78,8 +80,10 @@ struct hdmi_tx_ctrl { | |
struct work_struct hpd_int_work; | |
struct work_struct power_off_work; | |
+ struct work_struct cable_notify_work; | |
bool hdcp_feature_on; | |
+ bool ds_registered; | |
u32 present_hdcp; | |
u8 spd_vendor_name[9]; | |
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c | |
index 5c3b8d9..31e21f6 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp.c | |
@@ -1,7 +1,7 @@ | |
/* | |
* MDSS MDP Interface (used by framebuffer core) | |
* | |
- * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved. | |
+ * Copyright (c) 2007-2014, The Linux Foundation. All rights reserved. | |
* Copyright (C) 2007 Google Incorporated | |
* | |
* This software is licensed under the terms of the GNU General Public | |
@@ -56,6 +56,9 @@ | |
#include "mdss_panel.h" | |
#include "mdss_debug.h" | |
+#define CREATE_TRACE_POINTS | |
+#include "mdss_mdp_trace.h" | |
+ | |
struct mdss_data_type *mdss_res; | |
static int mdss_fb_mem_get_iommu_domain(void) | |
@@ -68,6 +71,7 @@ struct msm_mdp_interface mdp5 = { | |
.fb_mem_get_iommu_domain = mdss_fb_mem_get_iommu_domain, | |
.panel_register_done = mdss_panel_register_done, | |
.fb_stride = mdss_mdp_fb_stride, | |
+ .check_dsi_status = mdss_check_dsi_ctrl_status, | |
}; | |
#define DEFAULT_TOTAL_RGB_PIPES 3 | |
@@ -82,33 +86,12 @@ static DEFINE_MUTEX(mdp_clk_lock); | |
static DEFINE_MUTEX(bus_bw_lock); | |
static DEFINE_MUTEX(mdp_iommu_lock); | |
-#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ | |
- { \ | |
- .src = MSM_BUS_MASTER_MDP_PORT0, \ | |
- .dst = MSM_BUS_SLAVE_EBI_CH0, \ | |
- .ab = (ab_val), \ | |
- .ib = (ib_val), \ | |
- } | |
- | |
-static struct msm_bus_vectors mdp_bus_vectors[] = { | |
- MDP_BUS_VECTOR_ENTRY(0, 0), | |
- MDP_BUS_VECTOR_ENTRY(SZ_128M, SZ_256M), | |
- MDP_BUS_VECTOR_ENTRY(SZ_256M, SZ_512M), | |
-}; | |
-static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)]; | |
- | |
static struct mdss_panel_intf pan_types[] = { | |
{"dsi", MDSS_PANEL_INTF_DSI}, | |
{"edp", MDSS_PANEL_INTF_EDP}, | |
{"hdmi", MDSS_PANEL_INTF_HDMI}, | |
}; | |
-static struct msm_bus_scale_pdata mdp_bus_scale_table = { | |
- .usecase = mdp_bus_usecases, | |
- .num_usecases = ARRAY_SIZE(mdp_bus_usecases), | |
- .name = "mdss_mdp", | |
-}; | |
- | |
struct mdss_iommu_map_type mdss_iommu_map[MDSS_IOMMU_MAX_DOMAIN] = { | |
[MDSS_IOMMU_DOMAIN_UNSECURE] = { | |
.client_name = "mdp_ns", | |
@@ -154,8 +137,10 @@ static int mdss_mdp_parse_dt_handler(struct platform_device *pdev, | |
static int mdss_mdp_parse_dt_prop_len(struct platform_device *pdev, | |
char *prop_name); | |
static int mdss_mdp_parse_dt_smp(struct platform_device *pdev); | |
+static int mdss_mdp_parse_dt_prefill(struct platform_device *pdev); | |
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev); | |
static int mdss_mdp_parse_dt_ad_cfg(struct platform_device *pdev); | |
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev); | |
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp) | |
{ | |
@@ -315,6 +300,7 @@ void mdss_disable_irq_nosync(struct mdss_hw *hw) | |
pr_debug("Disable HW=%d irq ena=%d mask=%x\n", hw->hw_ndx, | |
mdss_res->irq_ena, mdss_res->irq_mask); | |
+ spin_lock(&mdss_lock); | |
if (!(mdss_res->irq_mask & ndx_bit)) { | |
pr_warn("MDSS HW ndx=%d is NOT set, mask=%x, hist mask=%x\n", | |
hw->hw_ndx, mdss_res->mdp_irq_mask, | |
@@ -326,29 +312,24 @@ void mdss_disable_irq_nosync(struct mdss_hw *hw) | |
disable_irq_nosync(mdss_res->irq); | |
} | |
} | |
+ spin_unlock(&mdss_lock); | |
} | |
EXPORT_SYMBOL(mdss_disable_irq_nosync); | |
static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata) | |
{ | |
if (!mdata->bus_hdl) { | |
- struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table; | |
- int i; | |
- | |
- for (i = 0; i < bus_pdata->num_usecases; i++) { | |
- mdp_bus_usecases[i].num_paths = 1; | |
- mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i]; | |
- } | |
- | |
- mdata->bus_hdl = msm_bus_scale_register_client(bus_pdata); | |
- if (!mdata->bus_hdl) { | |
- pr_err("not able to get bus scale\n"); | |
- return -ENOMEM; | |
+ mdata->bus_hdl = | |
+ msm_bus_scale_register_client(mdata->bus_scale_table); | |
+ if (IS_ERR_VALUE(mdata->bus_hdl)) { | |
+ pr_err("bus_client register failed\n"); | |
+ return -EINVAL; | |
} | |
pr_debug("register bus_hdl=%x\n", mdata->bus_hdl); | |
} | |
- return 0; | |
+ | |
+ return mdss_bus_scale_set_quota(MDSS_HW_MDP, AB_QUOTA, IB_QUOTA); | |
} | |
static void mdss_mdp_bus_scale_unregister(struct mdss_data_type *mdata) | |
@@ -361,7 +342,7 @@ static void mdss_mdp_bus_scale_unregister(struct mdss_data_type *mdata) | |
int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota) | |
{ | |
- int bus_idx; | |
+ int new_uc_idx; | |
if (mdss_res->bus_hdl < 1) { | |
pr_err("invalid bus handle %d\n", mdss_res->bus_hdl); | |
@@ -369,32 +350,73 @@ int mdss_mdp_bus_scale_set_quota(u64 ab_quota, u64 ib_quota) | |
} | |
if ((ab_quota | ib_quota) == 0) { | |
- bus_idx = 0; | |
+ new_uc_idx = 0; | |
} else { | |
- int num_cases = mdp_bus_scale_table.num_usecases; | |
+ int i; | |
struct msm_bus_vectors *vect = NULL; | |
+ struct msm_bus_scale_pdata *bw_table = | |
+ mdss_res->bus_scale_table; | |
+ unsigned long size; | |
+ | |
+ if (!bw_table || !mdss_res->axi_port_cnt) { | |
+ pr_err("invalid input\n"); | |
+ return -EINVAL; | |
+ } | |
- bus_idx = (mdss_res->current_bus_idx % (num_cases - 1)) + 1; | |
+ size = SZ_64M / mdss_res->axi_port_cnt; | |
- vect = mdp_bus_scale_table.usecase[mdss_res->current_bus_idx]. | |
- vectors; | |
+ ab_quota = div_u64(ab_quota, mdss_res->axi_port_cnt); | |
+ ib_quota = div_u64(ib_quota, mdss_res->axi_port_cnt); | |
- /* avoid performing updates for small changes */ | |
- if ((ALIGN(ab_quota, SZ_64M) == ALIGN(vect->ab, SZ_64M)) && | |
- (ALIGN(ib_quota, SZ_64M) == ALIGN(vect->ib, SZ_64M))) { | |
- pr_debug("skip bus scaling, no change in vectors\n"); | |
- return 0; | |
+ new_uc_idx = (mdss_res->curr_bw_uc_idx % | |
+ (bw_table->num_usecases - 1)) + 1; | |
+ | |
+ for (i = 0; i < mdss_res->axi_port_cnt; i++) { | |
+ vect = &bw_table->usecase[mdss_res->curr_bw_uc_idx]. | |
+ vectors[i]; | |
+ | |
+ /* avoid performing updates for small changes */ | |
+ if ((ALIGN(ab_quota, size) == ALIGN(vect->ab, size)) && | |
+ (ALIGN(ib_quota, size) == ALIGN(vect->ib, size))) { | |
+ pr_debug("skip bus scaling, no changes\n"); | |
+ return 0; | |
+ } | |
+ | |
+ vect = &bw_table->usecase[new_uc_idx].vectors[i]; | |
+ vect->ab = ab_quota; | |
+ vect->ib = ib_quota; | |
+ | |
+ pr_debug("uc_idx=%d path_idx=%d ab=%llu ib=%llu\n", | |
+ new_uc_idx, i, vect->ab, vect->ib); | |
} | |
+ } | |
+ mdss_res->curr_bw_uc_idx = new_uc_idx; | |
- vect = mdp_bus_scale_table.usecase[bus_idx].vectors; | |
- vect->ab = ab_quota; | |
- vect->ib = ib_quota; | |
+ return msm_bus_scale_client_update_request(mdss_res->bus_hdl, | |
+ new_uc_idx); | |
+} | |
+ | |
+int mdss_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota) | |
+{ | |
+ int rc = 0; | |
+ int i; | |
+ u64 total_ab = 0; | |
+ u64 total_ib = 0; | |
+ | |
+ mutex_lock(&bus_bw_lock); | |
- pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx, | |
- vect->ab, vect->ib); | |
+ mdss_res->ab[client] = ab_quota; | |
+ mdss_res->ib[client] = ib_quota; | |
+ for (i = 0; i < MDSS_MAX_HW_BLK; i++) { | |
+ total_ab += mdss_res->ab[i]; | |
+ total_ib = max(total_ib, mdss_res->ib[i]); | |
} | |
- mdss_res->current_bus_idx = bus_idx; | |
- return msm_bus_scale_client_update_request(mdss_res->bus_hdl, bus_idx); | |
+ | |
+ rc = mdss_mdp_bus_scale_set_quota(total_ab, total_ib); | |
+ | |
+ mutex_unlock(&bus_bw_lock); | |
+ | |
+ return rc; | |
} | |
static inline u32 mdss_mdp_irq_mask(u32 intr_type, u32 intf_num) | |
@@ -512,7 +534,16 @@ void mdss_mdp_hist_irq_disable(u32 irq) | |
spin_unlock_irqrestore(&mdp_lock, irq_flags); | |
} | |
-/* called from interrupt context */ | |
+/** | |
+ * mdss_mdp_irq_disable_nosync() - disable mdp irq | |
+ * @intr_type: mdp interface type | |
+ * @intf_num: mdp interface num | |
+ * | |
+ * This fucntion is called from interrupt context | |
+ * mdp_lock is already held at up stream (mdss_irq_handler) | |
+ * therefore spin_lock(&mdp_lock) is not allowed here | |
+ * | |
+*/ | |
void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num) | |
{ | |
u32 irq; | |
@@ -612,6 +643,37 @@ unsigned long mdss_mdp_get_clk_rate(u32 clk_idx) | |
return clk_rate; | |
} | |
+int mdss_iommu_ctrl(int enable) | |
+{ | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ int rc = 0; | |
+ | |
+ mutex_lock(&mdp_iommu_lock); | |
+ pr_debug("%pS: enable %d mdata->iommu_ref_cnt %d\n", | |
+ __builtin_return_address(0), enable, mdata->iommu_ref_cnt); | |
+ | |
+ if (enable) { | |
+ | |
+ if (mdata->iommu_ref_cnt == 0) | |
+ rc = mdss_iommu_attach(mdata); | |
+ mdata->iommu_ref_cnt++; | |
+ } else { | |
+ if (mdata->iommu_ref_cnt) { | |
+ mdata->iommu_ref_cnt--; | |
+ if (mdata->iommu_ref_cnt == 0) | |
+ rc = mdss_iommu_dettach(mdata); | |
+ } else { | |
+ pr_err("unbalanced iommu ref\n"); | |
+ } | |
+ } | |
+ mutex_unlock(&mdp_iommu_lock); | |
+ | |
+ if (IS_ERR_VALUE(rc)) | |
+ return rc; | |
+ else | |
+ return mdata->iommu_ref_cnt; | |
+} | |
+ | |
/** | |
* mdss_bus_bandwidth_ctrl() -- place bus bandwidth request | |
* @enable: value of enable or disable | |
@@ -619,7 +681,7 @@ unsigned long mdss_mdp_get_clk_rate(u32 clk_idx) | |
* Function place bus bandwidth request to allocate saved bandwidth | |
* if enabled or free bus bandwidth allocation if disabled. | |
* Bus bandwidth is required by mdp.For dsi, it only requires to send | |
- * dcs coammnd. | |
+ * dcs coammnd. It returns error if bandwidth request fails. | |
*/ | |
void mdss_bus_bandwidth_ctrl(int enable) | |
{ | |
@@ -649,14 +711,11 @@ void mdss_bus_bandwidth_ctrl(int enable) | |
if (!enable) { | |
msm_bus_scale_client_update_request( | |
mdata->bus_hdl, 0); | |
- mdss_iommu_dettach(mdata); | |
pm_runtime_put(&mdata->pdev->dev); | |
} else { | |
pm_runtime_get_sync(&mdata->pdev->dev); | |
msm_bus_scale_client_update_request( | |
- mdata->bus_hdl, mdata->current_bus_idx); | |
- if (!mdata->handoff_pending) | |
- mdss_iommu_attach(mdata); | |
+ mdata->bus_hdl, mdata->curr_bw_uc_idx); | |
} | |
} | |
@@ -685,6 +744,7 @@ void mdss_mdp_clk_ctrl(int enable, int isr) | |
} | |
} | |
+ MDSS_XLOG(mdp_clk_cnt, changed, enable, current->pid); | |
pr_debug("%s: clk_cnt=%d changed=%d enable=%d\n", | |
__func__, mdp_clk_cnt, changed, enable); | |
@@ -700,8 +760,6 @@ void mdss_mdp_clk_ctrl(int enable, int isr) | |
if (mdata->vsync_ena) | |
mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable); | |
- mdss_bus_bandwidth_ctrl(enable); | |
- | |
if (!enable) | |
pm_runtime_put(&mdata->pdev->dev); | |
} | |
@@ -785,13 +843,13 @@ int mdss_iommu_attach(struct mdss_data_type *mdata) | |
{ | |
struct iommu_domain *domain; | |
struct mdss_iommu_map_type *iomap; | |
- int i; | |
+ int i, rc = 0; | |
+ | |
+ MDSS_XLOG(mdata->iommu_attached); | |
- mutex_lock(&mdp_iommu_lock); | |
if (mdata->iommu_attached) { | |
pr_debug("mdp iommu already attached\n"); | |
- mutex_unlock(&mdp_iommu_lock); | |
- return 0; | |
+ goto end; | |
} | |
for (i = 0; i < MDSS_IOMMU_MAX_DOMAIN; i++) { | |
@@ -803,13 +861,21 @@ int mdss_iommu_attach(struct mdss_data_type *mdata) | |
iomap->client_name, iomap->ctx_name); | |
continue; | |
} | |
- iommu_attach_device(domain, iomap->ctx); | |
+ | |
+ rc = iommu_attach_device(domain, iomap->ctx); | |
+ if (rc) { | |
+ WARN(1, "mdp::iommu device attach failed rc:%d\n", rc); | |
+ for (i--; i >= 0; i--) { | |
+ iomap = mdata->iommu_map + i; | |
+ iommu_detach_device(domain, iomap->ctx); | |
+ } | |
+ goto end; | |
+ } | |
} | |
mdata->iommu_attached = true; | |
- mutex_unlock(&mdp_iommu_lock); | |
- | |
- return 0; | |
+end: | |
+ return rc; | |
} | |
int mdss_iommu_dettach(struct mdss_data_type *mdata) | |
@@ -818,10 +884,10 @@ int mdss_iommu_dettach(struct mdss_data_type *mdata) | |
struct mdss_iommu_map_type *iomap; | |
int i; | |
- mutex_lock(&mdp_iommu_lock); | |
+ MDSS_XLOG(mdata->iommu_attached); | |
+ | |
if (!mdata->iommu_attached) { | |
pr_debug("mdp iommu already dettached\n"); | |
- mutex_unlock(&mdp_iommu_lock); | |
return 0; | |
} | |
@@ -838,7 +904,6 @@ int mdss_iommu_dettach(struct mdss_data_type *mdata) | |
} | |
mdata->iommu_attached = false; | |
- mutex_unlock(&mdp_iommu_lock); | |
return 0; | |
} | |
@@ -961,7 +1026,7 @@ static int mdss_mdp_debug_init(struct mdss_data_type *mdata) | |
if (rc) | |
return rc; | |
- mdss_debug_register_base(NULL, mdata->mdp_base, mdata->mdp_reg_size); | |
+ mdss_debug_register_base("mdp", mdata->mdp_base, mdata->mdp_reg_size); | |
return 0; | |
} | |
@@ -1008,8 +1073,9 @@ int mdss_hw_init(struct mdss_data_type *mdata) | |
writel_relaxed(1, offset + 16); | |
} | |
- mdata->nmax_concurrent_ad_hw = (mdata->mdp_rev <= MDSS_MDP_HW_REV_102) ? | |
- 1 : 2; | |
+ mdata->nmax_concurrent_ad_hw = | |
+ (mdata->mdp_rev < MDSS_MDP_HW_REV_103) ? 1 : 2; | |
+ | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
pr_debug("MDP hw init done\n"); | |
@@ -1051,6 +1117,14 @@ static u32 mdss_mdp_res_init(struct mdss_data_type *mdata) | |
return rc; | |
} | |
+/** | |
+ * mdss_mdp_footswitch_ctrl_splash() - clocks handoff for cont. splash screen | |
+ * @on: 1 to start handoff, 0 to complete the handoff after first frame update | |
+ * | |
+ * MDSS Clocks and GDSC are already on during continous splash screen, but | |
+ * increasing ref count will keep clocks from being turned off until handoff | |
+ * has properly happend after frame update. | |
+ */ | |
void mdss_mdp_footswitch_ctrl_splash(int on) | |
{ | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
@@ -1059,9 +1133,11 @@ void mdss_mdp_footswitch_ctrl_splash(int on) | |
pr_debug("Enable MDP FS for splash.\n"); | |
mdata->handoff_pending = true; | |
regulator_enable(mdata->fs); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
mdss_hw_init(mdata); | |
} else { | |
pr_debug("Disable MDP FS for splash.\n"); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
regulator_disable(mdata->fs); | |
mdata->handoff_pending = false; | |
} | |
@@ -1087,6 +1163,7 @@ static ssize_t mdss_mdp_show_capabilities(struct device *dev, | |
SPRINT("dma_pipes=%d\n", mdata->ndma_pipes); | |
SPRINT("smp_count=%d\n", mdata->smp_mb_cnt); | |
SPRINT("smp_size=%d\n", mdata->smp_mb_size); | |
+ SPRINT("smp_mb_per_pipe=%d\n", mdata->smp_mb_per_pipe); | |
SPRINT("max_downscale_ratio=%d\n", MAX_DOWNSCALE_RATIO); | |
SPRINT("max_upscale_ratio=%d\n", MAX_UPSCALE_RATIO); | |
if (mdata->max_bw_low) | |
@@ -1098,6 +1175,8 @@ static ssize_t mdss_mdp_show_capabilities(struct device *dev, | |
SPRINT(" bwc"); | |
if (mdata->has_decimation) | |
SPRINT(" decimation"); | |
+ if (mdata->highest_bank_bit) | |
+ SPRINT(" tile_format"); | |
SPRINT("\n"); | |
return cnt; | |
@@ -1218,7 +1297,6 @@ static int mdss_mdp_probe(struct platform_device *pdev) | |
pr_err("unable to register bus scaling\n"); | |
goto probe_done; | |
} | |
- mdss_mdp_bus_scale_set_quota(AB_QUOTA, IB_QUOTA); | |
rc = mdss_mdp_debug_init(mdata); | |
if (rc) { | |
@@ -1539,6 +1617,12 @@ static int mdss_mdp_parse_dt(struct platform_device *pdev) | |
return rc; | |
} | |
+ rc = mdss_mdp_parse_dt_prefill(pdev); | |
+ if (rc) { | |
+ pr_err("Error in device tree : prefill\n"); | |
+ return rc; | |
+ } | |
+ | |
rc = mdss_mdp_parse_dt_misc(pdev); | |
if (rc) { | |
pr_err("Error in device tree : misc\n"); | |
@@ -1558,9 +1642,70 @@ static int mdss_mdp_parse_dt(struct platform_device *pdev) | |
return rc; | |
} | |
+ rc = mdss_mdp_parse_dt_bus_scale(pdev); | |
+ if (rc) { | |
+ pr_err("Error in device tree : bus scale\n"); | |
+ return rc; | |
+ } | |
+ | |
return 0; | |
} | |
+static int mdss_mdp_parse_dt_pipe_clk_ctrl(struct platform_device *pdev, | |
+ char *prop_name, struct mdss_mdp_pipe *pipe_list, u32 npipes) | |
+{ | |
+ int rc = 0; | |
+ size_t len; | |
+ const u32 *arr; | |
+ | |
+ arr = of_get_property(pdev->dev.of_node, prop_name, &len); | |
+ if (arr) { | |
+ int i, j; | |
+ | |
+ len /= sizeof(u32); | |
+ for (i = 0, j = 0; i < len; j++) { | |
+ struct mdss_mdp_pipe *pipe = NULL; | |
+ | |
+ if (j >= npipes) { | |
+ pr_err("invalid clk ctrl enries for prop: %s\n", | |
+ prop_name); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ pipe = &pipe_list[j]; | |
+ | |
+ pipe->clk_ctrl.reg_off = be32_to_cpu(arr[i++]); | |
+ pipe->clk_ctrl.bit_off = be32_to_cpu(arr[i++]); | |
+ | |
+ /* status register is next in line to ctrl register */ | |
+ pipe->clk_status.reg_off = pipe->clk_ctrl.reg_off + 4; | |
+ pipe->clk_status.bit_off = be32_to_cpu(arr[i++]); | |
+ | |
+ pr_debug("%s[%d]: ctrl: reg_off: 0x%x bit_off: %d\n", | |
+ prop_name, j, pipe->clk_ctrl.reg_off, | |
+ pipe->clk_ctrl.bit_off); | |
+ pr_debug("%s[%d]: status: reg_off: 0x%x bit_off: %d\n", | |
+ prop_name, j, pipe->clk_status.reg_off, | |
+ pipe->clk_status.bit_off); | |
+ } | |
+ if (j != npipes) { | |
+ pr_err("%s: %d entries found. required %d\n", | |
+ prop_name, j, npipes); | |
+ for (i = 0; i < npipes; i++) { | |
+ memset(&pipe_list[i].clk_ctrl, 0, | |
+ sizeof(pipe_list[i].clk_ctrl)); | |
+ memset(&pipe_list[i].clk_status, 0, | |
+ sizeof(pipe_list[i].clk_status)); | |
+ } | |
+ rc = -EINVAL; | |
+ } | |
+ } else { | |
+ pr_err("error mandatory property '%s' not found\n", prop_name); | |
+ rc = -EINVAL; | |
+ } | |
+ | |
+ return rc; | |
+} | |
static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev) | |
{ | |
@@ -1744,6 +1889,25 @@ static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev) | |
setup_cnt += mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES; | |
} | |
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev, | |
+ "qcom,mdss-pipe-vig-clk-ctrl-offsets", mdata->vig_pipes, | |
+ mdata->nvig_pipes); | |
+ if (rc) | |
+ goto parse_fail; | |
+ | |
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev, | |
+ "qcom,mdss-pipe-rgb-clk-ctrl-offsets", mdata->rgb_pipes, | |
+ mdata->nrgb_pipes); | |
+ if (rc) | |
+ goto parse_fail; | |
+ | |
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev, | |
+ "qcom,mdss-pipe-dma-clk-ctrl-offsets", mdata->dma_pipes, | |
+ mdata->ndma_pipes); | |
+ if (rc) | |
+ goto parse_fail; | |
+ | |
+ | |
goto parse_done; | |
parse_fail: | |
@@ -2014,6 +2178,84 @@ static int mdss_mdp_parse_dt_smp(struct platform_device *pdev) | |
return rc; | |
} | |
+static void mdss_mdp_parse_dt_fudge_factors(struct platform_device *pdev, | |
+ char *prop_name, struct mdss_fudge_factor *ff) | |
+{ | |
+ int rc; | |
+ u32 data[2] = {1, 1}; | |
+ | |
+ rc = mdss_mdp_parse_dt_handler(pdev, prop_name, data, 2); | |
+ if (rc) { | |
+ pr_err("err reading %s\n", prop_name); | |
+ } else { | |
+ ff->numer = data[0]; | |
+ ff->denom = data[1]; | |
+ } | |
+} | |
+ | |
+static int mdss_mdp_parse_dt_prefill(struct platform_device *pdev) | |
+{ | |
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev); | |
+ struct mdss_prefill_data *prefill = &mdata->prefill_data; | |
+ int rc; | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-outstanding-buffer-bytes", | |
+ &prefill->ot_bytes); | |
+ if (rc) { | |
+ pr_err("prefill outstanding buffer bytes not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-y-buffer-bytes", &prefill->y_buf_bytes); | |
+ if (rc) { | |
+ pr_err("prefill y buffer bytes not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-scaler-buffer-lines-bilinear", | |
+ &prefill->y_scaler_lines_bilinear); | |
+ if (rc) { | |
+ pr_err("prefill scaler lines for bilinear not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-scaler-buffer-lines-caf", | |
+ &prefill->y_scaler_lines_caf); | |
+ if (rc) { | |
+ pr_debug("prefill scaler lines for caf not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-post-scaler-buffer-pixels", | |
+ &prefill->post_scaler_pixels); | |
+ if (rc) { | |
+ pr_err("prefill post scaler buffer pixels not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-pingpong-buffer-pixels", | |
+ &prefill->pp_pixels); | |
+ if (rc) { | |
+ pr_err("prefill pingpong buffer lines not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-prefill-fbc-lines", &prefill->fbc_lines); | |
+ if (rc) { | |
+ pr_err("prefill FBC lines not specified\n"); | |
+ return rc; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) | |
{ | |
struct mdss_data_type *mdata = platform_get_drvdata(pdev); | |
@@ -2025,6 +2267,10 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) | |
&data); | |
mdata->rot_block_size = (!rc ? data : 128); | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-rotator-ot-limit", &data); | |
+ mdata->rotator_ot_limit = (!rc ? data : 0); | |
+ | |
mdata->has_bwc = of_property_read_bool(pdev->dev.of_node, | |
"qcom,mdss-has-bwc"); | |
mdata->has_decimation = of_property_read_bool(pdev->dev.of_node, | |
@@ -2035,6 +2281,45 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) | |
"qcom,mdss-no-lut-read"); | |
prop = of_find_property(pdev->dev.of_node, "batfet-supply", NULL); | |
mdata->batfet_required = prop ? true : false; | |
+ rc = of_property_read_u32(pdev->dev.of_node, | |
+ "qcom,mdss-highest-bank-bit", &(mdata->highest_bank_bit)); | |
+ if (rc) | |
+ pr_debug("Could not read optional property: highest bank bit\n"); | |
+ | |
+ /* | |
+ * 2x factor on AB because bus driver will divide by 2 | |
+ * due to 2x ports to BIMC | |
+ */ | |
+ mdata->ab_factor.numer = 2; | |
+ mdata->ab_factor.denom = 1; | |
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ab-factor", | |
+ &mdata->ab_factor); | |
+ | |
+ /* | |
+ * 1.2 factor on ib as default value. This value is | |
+ * experimentally determined and should be tuned in device | |
+ * tree. | |
+ */ | |
+ mdata->ib_factor.numer = 6; | |
+ mdata->ib_factor.denom = 5; | |
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ib-factor", | |
+ &mdata->ib_factor); | |
+ | |
+ /* | |
+ * Set overlap ib value equal to ib by default. This value can | |
+ * be tuned in device tree to be different from ib. | |
+ * This factor apply when the max bandwidth per pipe | |
+ * is the overlap BW. | |
+ */ | |
+ mdata->ib_factor_overlap.numer = mdata->ib_factor.numer; | |
+ mdata->ib_factor_overlap.denom = mdata->ib_factor.denom; | |
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-ib-factor-overlap", | |
+ &mdata->ib_factor_overlap); | |
+ | |
+ mdata->clk_factor.numer = 1; | |
+ mdata->clk_factor.denom = 1; | |
+ mdss_mdp_parse_dt_fudge_factors(pdev, "qcom,mdss-clk-factor", | |
+ &mdata->clk_factor); | |
rc = of_property_read_u32(pdev->dev.of_node, | |
"qcom,max-bandwidth-low-kbps", &mdata->max_bw_low); | |
@@ -2046,6 +2331,23 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) | |
if (rc) | |
pr_debug("max bandwidth (high) property not specified\n"); | |
+ mdata->nclk_lvl = mdss_mdp_parse_dt_prop_len(pdev, | |
+ "qcom,mdss-clk-levels"); | |
+ | |
+ if (mdata->nclk_lvl) { | |
+ mdata->clock_levels = kzalloc(sizeof(u32) * mdata->nclk_lvl, | |
+ GFP_KERNEL); | |
+ if (!mdata->clock_levels) { | |
+ pr_err("no mem assigned for mdata clock_levels\n"); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-clk-levels", | |
+ mdata->clock_levels, mdata->nclk_lvl); | |
+ if (rc) | |
+ pr_debug("clock levels not found\n"); | |
+ } | |
+ | |
return 0; | |
} | |
@@ -2089,6 +2391,31 @@ parse_done: | |
return rc; | |
} | |
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev) | |
+{ | |
+ int rc; | |
+ struct mdss_data_type *mdata = platform_get_drvdata(pdev); | |
+ | |
+ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-bus,num-paths", | |
+ &mdata->axi_port_cnt); | |
+ if (rc) { | |
+ pr_err("Error. qcom,msm-bus,num-paths prop not found.rc=%d\n", | |
+ rc); | |
+ return rc; | |
+ } | |
+ | |
+ mdata->bus_scale_table = msm_bus_cl_get_pdata(pdev); | |
+ if (IS_ERR_OR_NULL(mdata->bus_scale_table)) { | |
+ rc = PTR_ERR(mdata->bus_scale_table); | |
+ if (!rc) | |
+ rc = -EINVAL; | |
+ pr_err("msm_bus_cl_get_pdata failed. rc=%d\n", rc); | |
+ mdata->bus_scale_table = NULL; | |
+ } | |
+ | |
+ return rc; | |
+} | |
+ | |
static int mdss_mdp_parse_dt_handler(struct platform_device *pdev, | |
char *prop_name, u32 *offsets, int len) | |
{ | |
@@ -2255,22 +2582,60 @@ static void mdss_mdp_footswitch_ctrl(struct mdss_data_type *mdata, int on) | |
pr_debug("Enable MDP FS\n"); | |
if (!mdata->fs_ena) { | |
regulator_enable(mdata->fs); | |
- mdss_mdp_cx_ctrl(mdata, true); | |
- mdss_mdp_batfet_ctrl(mdata, true); | |
+ if (!mdata->ulps) { | |
+ mdss_mdp_cx_ctrl(mdata, true); | |
+ mdss_mdp_batfet_ctrl(mdata, true); | |
+ } | |
} | |
mdata->fs_ena = true; | |
} else { | |
pr_debug("Disable MDP FS\n"); | |
- mdss_iommu_dettach(mdata); | |
if (mdata->fs_ena) { | |
regulator_disable(mdata->fs); | |
- mdss_mdp_cx_ctrl(mdata, false); | |
- mdss_mdp_batfet_ctrl(mdata, false); | |
+ if (!mdata->ulps) { | |
+ mdss_mdp_cx_ctrl(mdata, false); | |
+ mdss_mdp_batfet_ctrl(mdata, false); | |
+ } | |
} | |
mdata->fs_ena = false; | |
} | |
} | |
+/** | |
+ * mdss_mdp_footswitch_ctrl_ulps() - MDSS GDSC control with ULPS feature | |
+ * @on: 1 to turn on footswitch, 0 to turn off footswitch | |
+ * @dev: framebuffer device node | |
+ * | |
+ * MDSS GDSC can be voted off during idle-screen usecase for MIPI DSI command | |
+ * mode displays with Ultra-Low Power State (ULPS) feature enabled. Upon | |
+ * subsequent frame update, MDSS GDSC needs to turned back on and hw state | |
+ * needs to be restored. It returns error if footswitch control API | |
+ * fails. | |
+ */ | |
+int mdss_mdp_footswitch_ctrl_ulps(int on, struct device *dev) | |
+{ | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ int rc = 0; | |
+ | |
+ pr_debug("called on=%d\n", on); | |
+ if (on) { | |
+ pm_runtime_get_sync(dev); | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) { | |
+ pr_err("mdss iommu attach failed rc=%d\n", rc); | |
+ return rc; | |
+ } | |
+ mdss_hw_init(mdata); | |
+ mdata->ulps = false; | |
+ mdss_iommu_ctrl(0); | |
+ } else { | |
+ mdata->ulps = true; | |
+ pm_runtime_put_sync(dev); | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
static inline int mdss_mdp_suspend_sub(struct mdss_data_type *mdata) | |
{ | |
mdata->suspend_fs_ena = mdata->fs_ena; | |
@@ -2352,11 +2717,12 @@ static int mdss_mdp_resume(struct platform_device *pdev) | |
static int mdss_mdp_runtime_resume(struct device *dev) | |
{ | |
struct mdss_data_type *mdata = dev_get_drvdata(dev); | |
+ bool device_on = true; | |
if (!mdata) | |
return -ENODEV; | |
dev_dbg(dev, "pm_runtime: resuming...\n"); | |
- | |
+ device_for_each_child(dev, &device_on, mdss_fb_suspres_panel); | |
mdss_mdp_footswitch_ctrl(mdata, true); | |
return 0; | |
@@ -2376,6 +2742,7 @@ static int mdss_mdp_runtime_idle(struct device *dev) | |
static int mdss_mdp_runtime_suspend(struct device *dev) | |
{ | |
struct mdss_data_type *mdata = dev_get_drvdata(dev); | |
+ bool device_on = false; | |
if (!mdata) | |
return -ENODEV; | |
dev_dbg(dev, "pm_runtime: suspending...\n"); | |
@@ -2384,6 +2751,7 @@ static int mdss_mdp_runtime_suspend(struct device *dev) | |
pr_err("MDP suspend failed\n"); | |
return -EBUSY; | |
} | |
+ device_for_each_child(dev, &device_on, mdss_fb_suspres_panel); | |
mdss_mdp_footswitch_ctrl(mdata, false); | |
return 0; | |
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h | |
index ae020bf..f5f5770 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp.h | |
+++ b/drivers/video/msm/mdss/mdss_mdp.h | |
@@ -19,6 +19,7 @@ | |
#include <linux/msm_mdp.h> | |
#include <linux/platform_device.h> | |
#include <linux/notifier.h> | |
+#include <linux/kref.h> | |
#include "mdss.h" | |
#include "mdss_mdp_hwio.h" | |
@@ -32,6 +33,7 @@ | |
#define MDP_CLK_DEFAULT_RATE 200000000 | |
#define PHASE_STEP_SHIFT 21 | |
#define MAX_MIXER_WIDTH 2048 | |
+#define MAX_LINE_BUFFER_WIDTH 2048 | |
#define MAX_MIXER_HEIGHT 0xFFFF | |
#define MAX_IMG_WIDTH 0x3FFF | |
#define MAX_IMG_HEIGHT 0x3FFF | |
@@ -75,6 +77,13 @@ static inline u32 mdss_mdp_reg_read(u32 addr) | |
#define MDSS_MDP_REG_WRITE(addr, val) MDSS_REG_WRITE((u32)(addr), (u32)(val)) | |
#define MDSS_MDP_REG_READ(addr) MDSS_REG_READ((u32)(addr)) | |
#endif | |
+#define PERF_STATUS_DONE 0 | |
+#define PERF_STATUS_BUSY 1 | |
+ | |
+enum mdss_mdp_perf_state_type { | |
+ PERF_SW_COMMIT_STATE = 0, | |
+ PERF_HW_MDP_STATE, | |
+}; | |
enum mdss_mdp_block_power_state { | |
MDP_BLOCK_POWER_OFF = 0, | |
@@ -126,6 +135,13 @@ struct splash_pipe_cfg { | |
struct mdss_mdp_ctl; | |
typedef void (*mdp_vsync_handler_t)(struct mdss_mdp_ctl *, ktime_t); | |
+struct mdss_mdp_img_rect { | |
+ u16 x; | |
+ u16 y; | |
+ u16 w; | |
+ u16 h; | |
+}; | |
+ | |
struct mdss_mdp_vsync_handler { | |
bool enabled; | |
bool cmd_post_flush; | |
@@ -138,6 +154,14 @@ enum mdss_mdp_wb_ctl_type { | |
MDSS_MDP_WB_CTL_TYPE_LINE | |
}; | |
+struct mdss_mdp_perf_params { | |
+ u64 bw_overlap; | |
+ u64 bw_prefill; | |
+ u32 prefill_bytes; | |
+ u64 bw_ctl; | |
+ u32 mdp_clk_rate; | |
+}; | |
+ | |
struct mdss_mdp_ctl { | |
u32 num; | |
char __iomem *base; | |
@@ -161,11 +185,11 @@ struct mdss_mdp_ctl { | |
u32 dst_format; | |
bool is_secure; | |
- u32 bus_ab_quota; | |
- u32 bus_ib_quota; | |
u32 clk_rate; | |
- u32 perf_changed; | |
int force_screen_state; | |
+ struct mdss_mdp_perf_params cur_perf; | |
+ struct mdss_mdp_perf_params new_perf; | |
+ u32 perf_transaction_status; | |
struct mdss_data_type *mdata; | |
struct msm_fb_data_type *mfd; | |
@@ -173,12 +197,16 @@ struct mdss_mdp_ctl { | |
struct mdss_mdp_mixer *mixer_right; | |
struct mutex lock; | |
struct mutex *shared_lock; | |
+ spinlock_t spin_lock; | |
struct mdss_panel_data *panel_data; | |
struct mdss_mdp_vsync_handler vsync_handler; | |
+ struct mdss_mdp_vsync_handler recover_underrun_handler; | |
+ struct work_struct recover_work; | |
+ struct work_struct remove_underrun_handler; | |
- struct mdss_rect roi; | |
- struct mdss_rect roi_bkup; | |
+ struct mdss_mdp_img_rect roi; | |
+ struct mdss_mdp_img_rect roi_bkup; | |
u8 roi_changed; | |
int (*start_fnc) (struct mdss_mdp_ctl *ctl); | |
@@ -192,7 +220,8 @@ struct mdss_mdp_ctl { | |
struct mdss_mdp_vsync_handler *); | |
int (*remove_vsync_handler) (struct mdss_mdp_ctl *, | |
struct mdss_mdp_vsync_handler *); | |
- int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl, int new_fps); | |
+ int (*config_fps_fnc) (struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_ctl *sctl, int new_fps); | |
struct blocking_notifier_head notifier_head; | |
@@ -210,7 +239,7 @@ struct mdss_mdp_mixer { | |
u8 params_changed; | |
u16 width; | |
u16 height; | |
- struct mdss_rect roi; | |
+ struct mdss_mdp_img_rect roi; | |
u8 cursor_enabled; | |
u8 rotator_mode; | |
@@ -231,7 +260,7 @@ struct mdss_mdp_format_params { | |
u8 unpack_count; /* 0 = 1 component, 1 = 2 component ... */ | |
u8 bpp; | |
u8 alpha_enable; /* source has alpha */ | |
- | |
+ u8 tile; | |
u8 bits[MAX_PLANES]; | |
u8 element[MAX_PLANES]; | |
}; | |
@@ -269,6 +298,7 @@ struct pp_hist_col_info { | |
u32 hist_cnt_time; | |
u32 frame_cnt; | |
struct completion comp; | |
+ struct completion first_kick; | |
u32 data[HIST_V_SIZE]; | |
struct mutex hist_mutex; | |
spinlock_t hist_lock; | |
@@ -300,9 +330,9 @@ struct mdss_ad_info { | |
u32 last_bl; | |
u32 bl_data; | |
u32 calc_itr; | |
- uint32_t bl_bright_shift; | |
uint32_t bl_lin[AD_BL_LIN_LEN]; | |
uint32_t bl_lin_inv[AD_BL_LIN_LEN]; | |
+ uint32_t bl_att_lut[AD_BL_ATT_LUT_LEN]; | |
}; | |
struct pp_sts_type { | |
@@ -332,6 +362,11 @@ struct mdss_mdp_pipe_smp_map { | |
DECLARE_BITMAP(fixed, MAX_DRV_SUP_MMB_BLKS); | |
}; | |
+struct mdss_mdp_shared_reg_ctrl { | |
+ u32 reg_off; | |
+ u32 bit_off; | |
+}; | |
+ | |
struct mdss_mdp_pipe { | |
u32 num; | |
u32 type; | |
@@ -339,7 +374,11 @@ struct mdss_mdp_pipe { | |
char __iomem *base; | |
u32 ftch_id; | |
u32 xin_id; | |
- atomic_t ref_cnt; | |
+ struct mdss_mdp_shared_reg_ctrl clk_ctrl; | |
+ struct mdss_mdp_shared_reg_ctrl clk_status; | |
+ | |
+ struct kref kref; | |
+ | |
u32 play_cnt; | |
int pid; | |
bool is_handed_off; | |
@@ -351,8 +390,8 @@ struct mdss_mdp_pipe { | |
u16 img_height; | |
u8 horz_deci; | |
u8 vert_deci; | |
- struct mdss_rect src; | |
- struct mdss_rect dst; | |
+ struct mdss_mdp_img_rect src; | |
+ struct mdss_mdp_img_rect dst; | |
struct mdss_mdp_format_params *src_fmt; | |
struct mdss_mdp_plane_sizes src_planes; | |
@@ -375,8 +414,7 @@ struct mdss_mdp_pipe { | |
struct mdss_mdp_data back_buf; | |
struct mdss_mdp_data front_buf; | |
- struct list_head used_list; | |
- struct list_head cleanup_list; | |
+ struct list_head list; | |
struct mdp_overlay_pp_params pp_cfg; | |
struct mdss_pipe_pp_res pp_res; | |
@@ -400,8 +438,11 @@ struct mdss_overlay_private { | |
struct mdss_data_type *mdata; | |
struct mutex ov_lock; | |
+ struct mutex dfps_lock; | |
struct mdss_mdp_ctl *ctl; | |
struct mdss_mdp_wb *wb; | |
+ | |
+ struct mutex list_lock; | |
struct list_head overlay_list; | |
struct list_head pipes_used; | |
struct list_head pipes_cleanup; | |
@@ -423,12 +464,6 @@ struct mdss_overlay_private { | |
int retire_cnt; | |
}; | |
-struct mdss_mdp_perf_params { | |
- u32 ib_quota; | |
- u32 ab_quota; | |
- u32 mdp_clk_rate; | |
-}; | |
- | |
/** | |
* enum mdss_screen_state - Screen states that MDP can be forced into | |
* | |
@@ -441,6 +476,16 @@ enum mdss_screen_state { | |
}; | |
#define is_vig_pipe(_pipe_id_) ((_pipe_id_) <= MDSS_MDP_SSPP_VIG2) | |
+ | |
+static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl( | |
+ struct mdss_mdp_ctl *ctl) | |
+{ | |
+ if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl)) | |
+ return ctl->mixer_right->ctl; | |
+ | |
+ return NULL; | |
+} | |
+ | |
static inline void mdss_mdp_ctl_write(struct mdss_mdp_ctl *ctl, | |
u32 reg, u32 val) | |
{ | |
@@ -463,6 +508,17 @@ static inline u32 mdss_mdp_pingpong_read(struct mdss_mdp_mixer *mixer, u32 reg) | |
return readl_relaxed(mixer->pingpong_base + reg); | |
} | |
+static inline int mdss_mdp_iommu_dyn_attach_supported( | |
+ struct mdss_data_type *mdata) | |
+{ | |
+ return (mdata->mdp_rev >= MDSS_MDP_HW_REV_103); | |
+} | |
+ | |
+static inline int mdss_mdp_line_buffer_width(void) | |
+{ | |
+ return MAX_LINE_BUFFER_WIDTH; | |
+} | |
+ | |
irqreturn_t mdss_mdp_isr(int irq, void *ptr); | |
int mdss_iommu_attach(struct mdss_data_type *mdata); | |
int mdss_iommu_dettach(struct mdss_data_type *mdata); | |
@@ -496,6 +552,12 @@ int mdss_mdp_overlay_get_buf(struct msm_fb_data_type *mfd, | |
struct msmfb_data *planes, | |
int num_planes, | |
u32 flags); | |
+int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
+ struct mdp_overlay *req, struct mdss_mdp_pipe **ppipe); | |
+void mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd, | |
+ u32 type); | |
+int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx); | |
+int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd); | |
int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata, | |
u32 *offsets, u32 count); | |
int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl); | |
@@ -518,9 +580,12 @@ int mdss_mdp_ctl_destroy(struct mdss_mdp_ctl *ctl); | |
int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff); | |
int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl); | |
int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg); | |
+int mdss_mdp_perf_bw_check(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_pipe **left_plist, int left_cnt, | |
+ struct mdss_mdp_pipe **right_plist, int right_cnt); | |
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe, | |
- struct mdss_mdp_perf_params *perf, struct mdss_rect *roi, | |
- int tune); | |
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi, | |
+ bool apply_fudge); | |
int mdss_mdp_ctl_notify(struct mdss_mdp_ctl *ctl, int event); | |
void mdss_mdp_ctl_notifier_register(struct mdss_mdp_ctl *ctl, | |
struct notifier_block *notifier); | |
@@ -529,8 +594,13 @@ void mdss_mdp_ctl_notifier_unregister(struct mdss_mdp_ctl *ctl, | |
int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num, | |
struct mdss_mdp_pipe *pipe); | |
+ | |
int mdss_mdp_scan_pipes(void); | |
+void mdss_mdp_ctl_perf_set_transaction_status(struct mdss_mdp_ctl *ctl, | |
+ enum mdss_mdp_perf_state_type component, bool new_status); | |
+void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl); | |
+ | |
struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator); | |
int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer); | |
struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux); | |
@@ -551,6 +621,7 @@ int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx, | |
int mdss_mdp_pp_init(struct device *dev); | |
void mdss_mdp_pp_term(struct device *dev); | |
+int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd); | |
int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 mixer_num); | |
@@ -599,6 +670,7 @@ int mdss_mdp_pipe_map(struct mdss_mdp_pipe *pipe); | |
void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe); | |
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_dma(struct mdss_mdp_mixer *mixer); | |
+u32 mdss_mdp_smp_get_size(struct mdss_mdp_pipe *pipe); | |
int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe); | |
void mdss_mdp_smp_unreserve(struct mdss_mdp_pipe *pipe); | |
void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe); | |
@@ -615,7 +687,6 @@ int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe); | |
int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe); | |
int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, | |
struct mdss_mdp_data *src_data); | |
-int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe); | |
int mdss_mdp_data_check(struct mdss_mdp_data *data, | |
struct mdss_mdp_plane_sizes *ps); | |
@@ -631,12 +702,12 @@ int mdss_mdp_get_img(struct msmfb_data *img, struct mdss_mdp_img_data *data); | |
int mdss_mdp_overlay_free_buf(struct mdss_mdp_data *data); | |
u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd); | |
int mdss_mdp_calc_phase_step(u32 src, u32 dst, u32 *out_phase); | |
-void mdss_mdp_intersect_rect(struct mdss_rect *res_rect, | |
- const struct mdss_rect *dst_rect, | |
- const struct mdss_rect *sci_rect); | |
-void mdss_mdp_crop_rect(struct mdss_rect *src_rect, | |
- struct mdss_rect *dst_rect, | |
- const struct mdss_rect *sci_rect); | |
+void mdss_mdp_intersect_rect(struct mdss_mdp_img_rect *res_rect, | |
+ const struct mdss_mdp_img_rect *dst_rect, | |
+ const struct mdss_mdp_img_rect *sci_rect); | |
+void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect, | |
+ struct mdss_mdp_img_rect *dst_rect, | |
+ const struct mdss_mdp_img_rect *sci_rect); | |
int mdss_mdp_wb_kickoff(struct msm_fb_data_type *mfd); | |
@@ -644,10 +715,7 @@ int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg); | |
int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id); | |
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp); | |
- | |
-#ifdef CONFIG_MACH_LGE | |
-int mdss_dsi_panel_invert(u32 enable); | |
-#endif | |
+void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval); | |
int mdss_panel_register_done(struct mdss_panel_data *pdata); | |
int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl); | |
@@ -662,12 +730,14 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_mixer_switch(struct mdss_mdp_ctl *ctl, | |
void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl, | |
struct mdp_display_commit *data); | |
-int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, int dst_format); | |
+int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, u32 dst_format); | |
int mdss_mdp_wb_get_format(struct msm_fb_data_type *mfd, | |
struct mdp_mixer_cfg *mixer_cfg); | |
int mdss_mdp_wb_set_secure(struct msm_fb_data_type *mfd, int enable); | |
int mdss_mdp_wb_get_secure(struct msm_fb_data_type *mfd, uint8_t *enable); | |
+void mdss_mdp_ctl_restore(struct mdss_mdp_ctl *ctl); | |
+int mdss_mdp_footswitch_ctrl_ulps(int on, struct device *dev); | |
int mdss_mdp_pipe_program_pixel_extn(struct mdss_mdp_pipe *pipe); | |
#define mfd_to_mdp5_data(mfd) (mfd->mdp.private1) | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c | |
index 2c18a58..b0b5a61 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c | |
@@ -18,30 +18,27 @@ | |
#include <linux/platform_device.h> | |
#include <linux/dma-mapping.h> | |
#include <linux/delay.h> | |
+#include <linux/sort.h> | |
#include "mdss_fb.h" | |
#include "mdss_mdp.h" | |
+#include "mdss_debug.h" | |
+#include "mdss_mdp_trace.h" | |
+#include "mdss_debug.h" | |
-/* truncate at 1k */ | |
-#define MDSS_MDP_BUS_FACTOR_SHIFT 10 | |
-/* 1.5 bus fudge factor */ | |
-#define MDSS_MDP_BUS_FUDGE_FACTOR_IB(val) (((val) / 2) * 3) | |
-#define MDSS_MDP_BUS_FUDGE_FACTOR_HIGH_IB(val) (val << 1) | |
-#define MDSS_MDP_BUS_FUDGE_FACTOR_AB(val) (val << 1) | |
-#define MDSS_MDP_BUS_FLOOR_BW (1600000000ULL >> MDSS_MDP_BUS_FACTOR_SHIFT) | |
- | |
-/* 1.25 clock fudge factor */ | |
-#define MDSS_MDP_CLK_FUDGE_FACTOR(val) (((val) * 5) / 4) | |
- | |
-enum { | |
- MDSS_MDP_PERF_UPDATE_SKIP, | |
- MDSS_MDP_PERF_UPDATE_EARLY, | |
- MDSS_MDP_PERF_UPDATE_LATE, | |
-}; | |
+static void mdss_mdp_xlog_mixer_reg(struct mdss_mdp_ctl *ctl); | |
+static inline u64 fudge_factor(u64 val, u32 numer, u32 denom) | |
+{ | |
+ u64 result = (val * (u64)numer); | |
+ do_div(result, denom); | |
+ return result; | |
+} | |
-#define MDSS_MDP_PERF_UPDATE_CLK BIT(0) | |
-#define MDSS_MDP_PERF_UPDATE_BUS BIT(1) | |
-#define MDSS_MDP_PERF_UPDATE_ALL -1 | |
+static inline u64 apply_fudge_factor(u64 val, | |
+ struct mdss_fudge_factor *factor) | |
+{ | |
+ return fudge_factor(val, factor->numer, factor->denom); | |
+} | |
static DEFINE_MUTEX(mdss_mdp_ctl_lock); | |
@@ -54,6 +51,11 @@ static inline void mdp_mixer_write(struct mdss_mdp_mixer *mixer, | |
writel_relaxed(val, mixer->base + reg); | |
} | |
+static inline u32 mdp_mixer_read(struct mdss_mdp_mixer *mixer, u32 reg) | |
+{ | |
+ return readl_relaxed(mixer->base + reg); | |
+} | |
+ | |
static inline u32 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl) | |
{ | |
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info; | |
@@ -63,112 +65,243 @@ static inline u32 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl) | |
pinfo->clk_rate; | |
} | |
-static u32 __mdss_mdp_ctrl_perf_ovrd_helper(struct mdss_mdp_mixer *mixer, | |
- u32 *npipe) | |
+static inline u32 mdss_mdp_clk_fudge_factor(struct mdss_mdp_mixer *mixer, | |
+ u32 rate) | |
{ | |
- struct mdss_panel_info *pinfo; | |
- struct mdss_mdp_pipe *pipe; | |
- u32 mnum, ovrd = 0; | |
+ struct mdss_panel_info *pinfo = &mixer->ctl->panel_data->panel_info; | |
- if (!mixer || !mixer->ctl->panel_data) | |
- return 0; | |
+ rate = apply_fudge_factor(rate, &mdss_res->clk_factor); | |
- pinfo = &mixer->ctl->panel_data->panel_info; | |
- for (mnum = 0; mnum < MDSS_MDP_MAX_STAGE; mnum++) { | |
- pipe = mixer->stage_pipe[mnum]; | |
- if (pipe && pinfo) { | |
- *npipe = *npipe + 1; | |
- if ((pipe->src.w >= pipe->src.h) && | |
- (pipe->src.w >= pinfo->xres)) | |
- ovrd = 1; | |
- } | |
- } | |
+ /* | |
+ * If the panel is video mode and its back porch period is | |
+ * small, the workaround of increasing mdp clk is needed to | |
+ * avoid underrun. | |
+ */ | |
+ if (mixer->ctl->is_video_mode && pinfo && | |
+ (pinfo->lcdc.v_back_porch < MDP_MIN_VBP)) | |
+ rate = apply_fudge_factor(rate, &mdss_res->clk_factor); | |
- return ovrd; | |
+ return rate; | |
} | |
-/** | |
- * mdss_mdp_ctrl_perf_ovrd() - Determines if performance override is needed | |
- * @mdata: Struct containing references to all MDP5 hardware structures | |
- * and status info such as interupts, target caps etc. | |
- * @ab_quota: Arbitrated bandwidth quota | |
- * @ib_quota: Instantaneous bandwidth quota | |
- * | |
- * Function calculates the minimum required MDP and BIMC clocks to avoid MDP | |
- * underflow during portrait video playback. The calculations are based on the | |
- * way MDP fetches (bandwidth requirement) and processes data through | |
- * MDP pipeline (MDP clock requirement) based on frame size and scaling | |
- * requirements. | |
- */ | |
-static void __mdss_mdp_ctrl_perf_ovrd(struct mdss_data_type *mdata, | |
- u64 *ab_quota, u64 *ib_quota) | |
+struct mdss_mdp_prefill_params { | |
+ u32 smp_bytes; | |
+ u32 xres; | |
+ u32 src_w; | |
+ u32 dst_w; | |
+ u32 src_h; | |
+ u32 dst_h; | |
+ u32 dst_y; | |
+ u32 bpp; | |
+ bool is_yuv; | |
+ bool is_caf; | |
+ bool is_fbc; | |
+ bool is_bwc; | |
+ bool is_tile; | |
+ bool is_hflip; | |
+}; | |
+ | |
+static inline bool mdss_mdp_perf_is_caf(struct mdss_mdp_pipe *pipe) | |
{ | |
- struct mdss_mdp_ctl *ctl; | |
- u32 i, npipe = 0, ovrd = 0; | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
- for (i = 0; i < mdata->nctl; i++) { | |
- ctl = mdata->ctl_off + i; | |
- if (!ctl->power_on) | |
- continue; | |
- ovrd |= __mdss_mdp_ctrl_perf_ovrd_helper( | |
- ctl->mixer_left, &npipe); | |
- ovrd |= __mdss_mdp_ctrl_perf_ovrd_helper( | |
- ctl->mixer_right, &npipe); | |
+ /* | |
+ * CAF mode filter is enabled when format is yuv and | |
+ * upscaling. Post processing had the decision to use CAF | |
+ * under these conditions. | |
+ */ | |
+ return ((mdata->mdp_rev >= MDSS_MDP_HW_REV_102) && | |
+ pipe->src_fmt->is_yuv && ((pipe->src.h >> pipe->vert_deci) <= | |
+ pipe->dst.h)); | |
+} | |
+ | |
+static inline u32 mdss_mdp_calc_y_scaler_bytes(struct mdss_mdp_prefill_params | |
+ *params, struct mdss_prefill_data *prefill) | |
+{ | |
+ u32 y_scaler_bytes = 0, y_scaler_lines = 0; | |
+ | |
+ if (params->is_yuv) { | |
+ if (params->src_h != params->dst_h) { | |
+ y_scaler_lines = (params->is_caf) ? | |
+ prefill->y_scaler_lines_caf : | |
+ prefill->y_scaler_lines_bilinear; | |
+ /* | |
+ * y is src_width, u is src_width/2 and v is | |
+ * src_width/2, so the total is scaler_lines * | |
+ * src_w * 2 | |
+ */ | |
+ y_scaler_bytes = y_scaler_lines * params->src_w * 2; | |
+ } | |
+ } else { | |
+ if (params->src_h != params->dst_h) { | |
+ y_scaler_lines = prefill->y_scaler_lines_bilinear; | |
+ y_scaler_bytes = y_scaler_lines * params->src_w * | |
+ params->bpp; | |
+ } | |
} | |
- *ab_quota = MDSS_MDP_BUS_FUDGE_FACTOR_AB(*ab_quota); | |
- if (npipe > 1) | |
- *ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_HIGH_IB(*ib_quota); | |
- else | |
- *ib_quota = MDSS_MDP_BUS_FUDGE_FACTOR_IB(*ib_quota); | |
+ return y_scaler_bytes; | |
+} | |
+ | |
+static inline u32 mdss_mdp_calc_latency_buf_bytes(struct mdss_mdp_prefill_params | |
+ *params, struct mdss_prefill_data *prefill) | |
+{ | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ u32 latency_lines, latency_buf_bytes; | |
- if (ovrd && (*ib_quota < MDSS_MDP_BUS_FLOOR_BW)) { | |
- *ib_quota = MDSS_MDP_BUS_FLOOR_BW; | |
- pr_debug("forcing the BIMC clock to 200 MHz : %llu bytes", | |
- *ib_quota); | |
+ if (params->is_yuv) { | |
+ if (params->is_bwc) { | |
+ latency_lines = 4; | |
+ latency_buf_bytes = params->src_w * params->bpp * | |
+ latency_lines; | |
+ } else { | |
+ latency_lines = 2; | |
+ latency_buf_bytes = ALIGN(params->src_w * params->bpp * | |
+ latency_lines, mdata->smp_mb_size) * 2; | |
+ } | |
} else { | |
- pr_debug("ib quota : %llu bytes", *ib_quota); | |
+ if (params->is_tile) { | |
+ latency_lines = 8; | |
+ latency_buf_bytes = params->src_w * params->bpp * | |
+ latency_lines; | |
+ } else if (params->is_bwc) { | |
+ latency_lines = 4; | |
+ latency_buf_bytes = params->src_w * params->bpp * | |
+ latency_lines; | |
+ } else { | |
+ latency_lines = 2; | |
+ latency_buf_bytes = ALIGN(params->src_w * params->bpp * | |
+ latency_lines, mdata->smp_mb_size); | |
+ } | |
} | |
+ | |
+ return latency_buf_bytes; | |
} | |
-static int mdss_mdp_ctl_perf_commit(struct mdss_data_type *mdata, u32 flags) | |
+static inline u32 mdss_mdp_calc_scaling_w_h(u32 val, u32 src_h, u32 dst_h, | |
+ u32 src_w, u32 dst_w) | |
{ | |
- struct mdss_mdp_ctl *ctl; | |
- int cnum; | |
- unsigned long clk_rate = 0; | |
- u64 bus_ab_quota = 0, bus_ib_quota = 0; | |
+ if (dst_h) | |
+ val = mult_frac(val, src_h, dst_h); | |
+ if (dst_w) | |
+ val = mult_frac(val, src_w, dst_w); | |
- if (!flags) { | |
- pr_err("nothing to update\n"); | |
- return -EINVAL; | |
- } | |
+ return val; | |
+} | |
- mutex_lock(&mdss_mdp_ctl_lock); | |
- for (cnum = 0; cnum < mdata->nctl; cnum++) { | |
- ctl = mdata->ctl_off + cnum; | |
- if (ctl->power_on) { | |
- bus_ab_quota += ctl->bus_ab_quota; | |
- bus_ib_quota += ctl->bus_ib_quota; | |
+static u32 mdss_mdp_perf_calc_pipe_prefill_video(struct mdss_mdp_prefill_params | |
+ *params) | |
+{ | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ struct mdss_prefill_data *prefill = &mdata->prefill_data; | |
+ u32 prefill_bytes; | |
+ u32 latency_buf_bytes; | |
+ u32 y_buf_bytes = 0; | |
+ u32 y_scaler_bytes; | |
+ u32 pp_bytes = 0, pp_lines = 0; | |
+ u32 post_scaler_bytes; | |
+ u32 fbc_bytes = 0; | |
- if (ctl->clk_rate > clk_rate) | |
- clk_rate = ctl->clk_rate; | |
- } | |
- } | |
- if (flags & MDSS_MDP_PERF_UPDATE_BUS) { | |
- bus_ab_quota = bus_ib_quota; | |
- __mdss_mdp_ctrl_perf_ovrd(mdata, &bus_ab_quota, &bus_ib_quota); | |
- bus_ib_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT; | |
- bus_ab_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT; | |
- mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota); | |
+ prefill_bytes = prefill->ot_bytes; | |
+ | |
+ latency_buf_bytes = mdss_mdp_calc_latency_buf_bytes(params, prefill); | |
+ prefill_bytes += latency_buf_bytes; | |
+ pr_debug("latency_buf_bytes bw_calc=%d actual=%d\n", latency_buf_bytes, | |
+ params->smp_bytes); | |
+ | |
+ if (params->is_yuv) | |
+ y_buf_bytes = prefill->y_buf_bytes; | |
+ | |
+ y_scaler_bytes = mdss_mdp_calc_y_scaler_bytes(params, prefill); | |
+ | |
+ prefill_bytes += y_buf_bytes + y_scaler_bytes; | |
+ | |
+ post_scaler_bytes = prefill->post_scaler_pixels * params->bpp; | |
+ post_scaler_bytes = mdss_mdp_calc_scaling_w_h(post_scaler_bytes, | |
+ params->src_h, params->dst_h, params->src_w, params->dst_w); | |
+ prefill_bytes += post_scaler_bytes; | |
+ | |
+ if (params->xres) | |
+ pp_lines = DIV_ROUND_UP(prefill->pp_pixels, params->xres); | |
+ if (params->xres && params->dst_h && (params->dst_y <= pp_lines)) | |
+ pp_bytes = ((params->src_w * params->bpp * prefill->pp_pixels / | |
+ params->xres) * params->src_h) / params->dst_h; | |
+ prefill_bytes += pp_bytes; | |
+ | |
+ if (params->is_fbc) { | |
+ fbc_bytes = prefill->fbc_lines * params->bpp; | |
+ fbc_bytes = mdss_mdp_calc_scaling_w_h(fbc_bytes, params->src_h, | |
+ params->dst_h, params->src_w, params->dst_w); | |
} | |
- if (flags & MDSS_MDP_PERF_UPDATE_CLK) { | |
- clk_rate = MDSS_MDP_CLK_FUDGE_FACTOR(clk_rate); | |
- pr_debug("update clk rate = %lu HZ\n", clk_rate); | |
- mdss_mdp_set_clk_rate(clk_rate); | |
+ prefill_bytes += fbc_bytes; | |
+ | |
+ pr_debug("ot=%d y_buf=%d pp_lines=%d pp=%d post_sc=%d fbc_bytes=%d\n", | |
+ prefill->ot_bytes, y_buf_bytes, pp_lines, pp_bytes, | |
+ post_scaler_bytes, fbc_bytes); | |
+ | |
+ return prefill_bytes; | |
+} | |
+ | |
+static u32 mdss_mdp_perf_calc_pipe_prefill_cmd(struct mdss_mdp_prefill_params | |
+ *params) | |
+{ | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ struct mdss_prefill_data *prefill = &mdata->prefill_data; | |
+ u32 prefill_bytes; | |
+ u32 ot_bytes = 0; | |
+ u32 latency_lines, latency_buf_bytes; | |
+ u32 y_buf_bytes = 0; | |
+ u32 y_scaler_bytes; | |
+ u32 fbc_cmd_lines = 0, fbc_cmd_bytes = 0; | |
+ u32 post_scaler_bytes = 0; | |
+ | |
+ /* y_scaler_bytes are same for the first or non first line */ | |
+ y_scaler_bytes = mdss_mdp_calc_y_scaler_bytes(params, prefill); | |
+ prefill_bytes = y_scaler_bytes; | |
+ | |
+ /* 1st line if fbc is not enabled and 2nd line if fbc is enabled */ | |
+ if (((params->dst_y == 0) && !params->is_fbc) || | |
+ ((params->dst_y <= 1) && params->is_fbc)) { | |
+ if (params->is_bwc || params->is_tile) | |
+ latency_lines = 4; | |
+ else if (!params->is_caf && params->is_hflip) | |
+ latency_lines = 1; | |
+ else | |
+ latency_lines = 0; | |
+ latency_buf_bytes = params->src_w * params->bpp * latency_lines; | |
+ prefill_bytes += latency_buf_bytes; | |
+ | |
+ fbc_cmd_lines++; | |
+ if (params->is_fbc) | |
+ fbc_cmd_lines++; | |
+ fbc_cmd_bytes = params->bpp * params->dst_w * fbc_cmd_lines; | |
+ fbc_cmd_bytes = mdss_mdp_calc_scaling_w_h(fbc_cmd_bytes, | |
+ params->src_h, params->dst_h, params->src_w, | |
+ params->dst_w); | |
+ prefill_bytes += fbc_cmd_bytes; | |
+ } else { | |
+ ot_bytes = prefill->ot_bytes; | |
+ prefill_bytes += ot_bytes; | |
+ | |
+ latency_buf_bytes = mdss_mdp_calc_latency_buf_bytes(params, | |
+ prefill); | |
+ prefill_bytes += latency_buf_bytes; | |
+ | |
+ if (params->is_yuv) | |
+ y_buf_bytes = prefill->y_buf_bytes; | |
+ prefill_bytes += y_buf_bytes; | |
+ | |
+ post_scaler_bytes = prefill->post_scaler_pixels * params->bpp; | |
+ post_scaler_bytes = mdss_mdp_calc_scaling_w_h(post_scaler_bytes, | |
+ params->src_h, params->dst_h, params->src_w, | |
+ params->dst_w); | |
+ prefill_bytes += post_scaler_bytes; | |
} | |
- mutex_unlock(&mdss_mdp_ctl_lock); | |
- return 0; | |
+ pr_debug("ot=%d bwc=%d smp=%d y_buf=%d fbc=%d\n", ot_bytes, | |
+ params->is_bwc, latency_buf_bytes, y_buf_bytes, fbc_cmd_bytes); | |
+ | |
+ return prefill_bytes; | |
} | |
/** | |
@@ -176,6 +309,7 @@ static int mdss_mdp_ctl_perf_commit(struct mdss_data_type *mdata, u32 flags) | |
* @pipe: Source pipe struct containing updated pipe params | |
* @perf: Structure containing values that should be updated for | |
* performance tuning | |
+ * @apply_fudge: Boolean to determine if mdp clock fudge is applicable | |
* | |
* Function calculates the minimum required performance calculations in order | |
* to avoid MDP underflow. The calculations are based on the way MDP | |
@@ -183,12 +317,15 @@ static int mdss_mdp_ctl_perf_commit(struct mdss_data_type *mdata, u32 flags) | |
* (MDP clock requirement) based on frame size and scaling requirements. | |
*/ | |
int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe, | |
- struct mdss_mdp_perf_params *perf, struct mdss_rect *roi, int tune) | |
+ struct mdss_mdp_perf_params *perf, struct mdss_mdp_img_rect *roi, | |
+ bool apply_fudge) | |
{ | |
struct mdss_mdp_mixer *mixer; | |
int fps = DEFAULT_FRAME_RATE; | |
- u32 quota, rate, v_total, src_h; | |
- struct mdss_rect src, dst; | |
+ u32 quota, rate, v_total, src_h, xres = 0; | |
+ struct mdss_mdp_img_rect src, dst; | |
+ bool is_fbc = false; | |
+ struct mdss_mdp_prefill_params prefill_params; | |
if (!pipe || !perf || !pipe->mixer) | |
return -EINVAL; | |
@@ -203,24 +340,38 @@ int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe, | |
struct mdss_panel_info *pinfo; | |
pinfo = &mixer->ctl->panel_data->panel_info; | |
- fps = mdss_panel_get_framerate(pinfo); | |
- v_total = mdss_panel_get_vtotal_lcd(pinfo, | |
- tune ? &pinfo->lcdc_tune : &pinfo->lcdc); | |
+ if (pinfo->type == MIPI_VIDEO_PANEL) { | |
+ fps = pinfo->panel_max_fps; | |
+ v_total = pinfo->panel_max_vtotal; | |
+ } else { | |
+ fps = mdss_panel_get_framerate(pinfo); | |
+ v_total = mdss_panel_get_vtotal(pinfo); | |
+ } | |
+ xres = pinfo->xres; | |
+ is_fbc = pinfo->fbc.enabled; | |
} else { | |
v_total = mixer->height; | |
+ xres = mixer->width; | |
} | |
if (roi) | |
mdss_mdp_crop_rect(&src, &dst, roi); | |
+ pr_debug("v_total=%d, xres=%d fps=%d\n", v_total, xres, fps); | |
+ | |
/* | |
* when doing vertical decimation lines will be skipped, hence there is | |
* no need to account for these lines in MDP clock or request bus | |
* bandwidth to fetch them. | |
*/ | |
- src_h = src.h >> pipe->vert_deci; | |
+ src_h = DECIMATED_DIMENSION(src.h, pipe->vert_deci); | |
quota = fps * src.w * src_h; | |
+ | |
+ pr_debug("src(w,h)(%d,%d) dst(w,h)(%d,%d) dst_y=%d bpp=%d yuv=%d\n", | |
+ pipe->src.w, src_h, pipe->dst.w, pipe->dst.h, pipe->dst.y, | |
+ pipe->src_fmt->bpp, pipe->src_fmt->is_yuv); | |
+ | |
if (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) | |
/* | |
* with decimation, chroma is not downsampled, this means we | |
@@ -241,140 +392,635 @@ int mdss_mdp_perf_calc_pipe(struct mdss_mdp_pipe *pipe, | |
if (mixer->rotator_mode) { | |
rate /= 4; /* block mode fetch at 4 pix/clk */ | |
quota *= 2; /* bus read + write */ | |
- perf->ib_quota = quota; | |
+ perf->bw_overlap = quota; | |
} else { | |
- perf->ib_quota = (quota / dst.h) * v_total; | |
+ perf->bw_overlap = (quota / dst.h) * v_total; | |
} | |
- perf->ab_quota = quota; | |
- perf->mdp_clk_rate = rate; | |
+ if (apply_fudge) | |
+ perf->mdp_clk_rate = mdss_mdp_clk_fudge_factor(mixer, rate); | |
+ else | |
+ perf->mdp_clk_rate = rate; | |
+ | |
+ prefill_params.smp_bytes = mdss_mdp_smp_get_size(pipe); | |
+ prefill_params.xres = xres; | |
+ prefill_params.src_w = src.w; | |
+ prefill_params.src_h = src_h; | |
+ prefill_params.dst_w = dst.w; | |
+ prefill_params.dst_h = dst.h; | |
+ prefill_params.dst_y = dst.y; | |
+ prefill_params.bpp = pipe->src_fmt->bpp; | |
+ prefill_params.is_yuv = pipe->src_fmt->is_yuv; | |
+ prefill_params.is_caf = mdss_mdp_perf_is_caf(pipe); | |
+ prefill_params.is_fbc = is_fbc; | |
+ prefill_params.is_bwc = pipe->bwc_mode; | |
+ prefill_params.is_tile = pipe->src_fmt->tile; | |
+ prefill_params.is_hflip = pipe->flags & MDP_FLIP_LR; | |
+ | |
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) { | |
+ perf->prefill_bytes = (mixer->ctl->is_video_mode) ? | |
+ mdss_mdp_perf_calc_pipe_prefill_video(&prefill_params) : | |
+ mdss_mdp_perf_calc_pipe_prefill_cmd(&prefill_params); | |
+ } | |
+ else | |
+ perf->prefill_bytes = 0; | |
- pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n", | |
- mixer->num, pipe->num, rate, perf->ab_quota, perf->ib_quota); | |
+ pr_debug("mixer=%d pnum=%d clk_rate=%u bw_overlap=%llu prefill=%d\n", | |
+ mixer->num, pipe->num, perf->mdp_clk_rate, perf->bw_overlap, | |
+ perf->prefill_bytes); | |
return 0; | |
} | |
-static void mdss_mdp_perf_mixer_update(struct mdss_mdp_mixer *mixer, | |
- u32 *bus_ab_quota, u32 *bus_ib_quota, | |
- u32 *clk_rate) | |
+static inline int mdss_mdp_perf_is_overlap(u32 y00, u32 y01, u32 y10, u32 y11) | |
+{ | |
+ return (y10 < y00 && y11 >= y01) || (y10 >= y00 && y10 < y01); | |
+} | |
+ | |
+static inline int cmpu32(const void *a, const void *b) | |
+{ | |
+ return (*(u32 *)a < *(u32 *)b) ? -1 : 0; | |
+} | |
+ | |
+static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer, | |
+ struct mdss_mdp_perf_params *perf, | |
+ struct mdss_mdp_pipe **pipe_list, int num_pipes) | |
{ | |
struct mdss_mdp_pipe *pipe; | |
struct mdss_panel_info *pinfo = NULL; | |
int fps = DEFAULT_FRAME_RATE; | |
- u32 v_total; | |
+ u32 v_total = 0; | |
int i; | |
- u32 max_clk_rate = 0, ab_total = 0, ib_total = 0; | |
+ u32 max_clk_rate = 0; | |
+ u64 bw_overlap_max = 0; | |
+ u64 bw_overlap[MDSS_MDP_MAX_STAGE] = { 0 }; | |
+ u32 v_region[MDSS_MDP_MAX_STAGE * 2] = { 0 }; | |
+ u32 prefill_bytes = 0; | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ bool apply_fudge = true; | |
+ | |
+ BUG_ON(num_pipes > MDSS_MDP_MAX_STAGE); | |
- *bus_ab_quota = 0; | |
- *bus_ib_quota = 0; | |
- *clk_rate = 0; | |
+ memset(perf, 0, sizeof(*perf)); | |
if (!mixer->rotator_mode) { | |
if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) { | |
pinfo = &mixer->ctl->panel_data->panel_info; | |
- fps = mdss_panel_get_framerate(pinfo); | |
- v_total = mdss_panel_get_vtotal(pinfo); | |
+ if (pinfo->type == MIPI_VIDEO_PANEL) { | |
+ fps = pinfo->panel_max_fps; | |
+ v_total = pinfo->panel_max_vtotal; | |
+ } else { | |
+ fps = mdss_panel_get_framerate(pinfo); | |
+ v_total = mdss_panel_get_vtotal(pinfo); | |
+ } | |
if (pinfo->type == WRITEBACK_PANEL) | |
pinfo = NULL; | |
} else { | |
v_total = mixer->height; | |
} | |
- *clk_rate = mixer->width * v_total * fps; | |
- if (pinfo && pinfo->lcdc.v_back_porch < MDP_MIN_VBP) | |
- *clk_rate = MDSS_MDP_CLK_FUDGE_FACTOR(*clk_rate); | |
- | |
- if (!pinfo) { | |
- /* perf for bus writeback */ | |
- *bus_ab_quota = fps * mixer->width * mixer->height * 3; | |
- *bus_ab_quota >>= MDSS_MDP_BUS_FACTOR_SHIFT; | |
- *bus_ib_quota = *bus_ab_quota; | |
+ | |
+ perf->mdp_clk_rate = mixer->width * v_total * fps; | |
+ perf->mdp_clk_rate = | |
+ mdss_mdp_clk_fudge_factor(mixer, perf->mdp_clk_rate); | |
+ | |
+ if (!pinfo) /* perf for bus writeback */ | |
+ perf->bw_overlap = | |
+ fps * mixer->width * mixer->height * 3; | |
+ } | |
+ | |
+ memset(bw_overlap, 0, sizeof(u64) * MDSS_MDP_MAX_STAGE); | |
+ memset(v_region, 0, sizeof(u32) * MDSS_MDP_MAX_STAGE * 2); | |
+ | |
+ /* | |
+ * Apply this logic only for 8x26 to reduce clock rate | |
+ * for single video playback use case | |
+ */ | |
+ if (IS_MDSS_MAJOR_MINOR_SAME(mdata->mdp_rev, MDSS_MDP_HW_REV_101) | |
+ && mixer->type == MDSS_MDP_MIXER_TYPE_INTF) { | |
+ u32 npipes = 0; | |
+ for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) { | |
+ pipe = mixer->stage_pipe[i]; | |
+ if (pipe) { | |
+ if (npipes) { | |
+ apply_fudge = true; | |
+ break; | |
+ } | |
+ npipes++; | |
+ apply_fudge = !(pipe->src_fmt->is_yuv) | |
+ || !(pipe->flags | |
+ & MDP_SOURCE_ROTATED_90); | |
+ } | |
} | |
} | |
- for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) { | |
- struct mdss_mdp_perf_params perf; | |
- pipe = mixer->stage_pipe[i]; | |
+ for (i = 0; i < num_pipes; i++) { | |
+ struct mdss_mdp_perf_params tmp; | |
+ pipe = pipe_list[i]; | |
if (pipe == NULL) | |
continue; | |
- if (mdss_mdp_perf_calc_pipe(pipe, &perf, &mixer->roi, 0)) | |
+ if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi, | |
+ apply_fudge)) | |
continue; | |
+ prefill_bytes += tmp.prefill_bytes; | |
+ bw_overlap[i] = tmp.bw_overlap; | |
+ v_region[2*i] = pipe->dst.y; | |
+ v_region[2*i + 1] = pipe->dst.y + pipe->dst.h; | |
+ if (tmp.mdp_clk_rate > max_clk_rate) | |
+ max_clk_rate = tmp.mdp_clk_rate; | |
+ } | |
- ab_total += perf.ab_quota >> MDSS_MDP_BUS_FACTOR_SHIFT; | |
- ib_total += perf.ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT; | |
- if (perf.mdp_clk_rate > max_clk_rate) | |
- max_clk_rate = perf.mdp_clk_rate; | |
+ /* | |
+ * Sort the v_region array so the total display area can be | |
+ * divided into individual regions. Check how many pipes fetch | |
+ * data for each region and sum them up, then the worst case | |
+ * of all regions is ib request. | |
+ */ | |
+ sort(v_region, num_pipes * 2, sizeof(u32), cmpu32, NULL); | |
+ for (i = 1; i < num_pipes * 2; i++) { | |
+ int j; | |
+ u64 bw_max_region = 0; | |
+ u32 y0, y1; | |
+ pr_debug("v_region[%d]%d\n", i, v_region[i]); | |
+ if (v_region[i] == v_region[i-1]) | |
+ continue; | |
+ y0 = v_region[i-1]; | |
+ y1 = v_region[i]; | |
+ for (j = 0; j < num_pipes; j++) { | |
+ if (!bw_overlap[j]) | |
+ continue; | |
+ pipe = pipe_list[j]; | |
+ if (mdss_mdp_perf_is_overlap(y0, y1, pipe->dst.y, | |
+ (pipe->dst.y + pipe->dst.h))) | |
+ bw_max_region += bw_overlap[j]; | |
+ pr_debug("v[%d](%d,%d)pipe[%d](%d,%d)bw(%llu %llu)\n", | |
+ i, y0, y1, j, pipe->dst.y, | |
+ pipe->dst.y + pipe->dst.h, bw_overlap[j], | |
+ bw_max_region); | |
+ } | |
+ bw_overlap_max = max(bw_overlap_max, bw_max_region); | |
} | |
- *bus_ab_quota += ab_total; | |
- *bus_ib_quota += ib_total; | |
- if (max_clk_rate > *clk_rate) | |
- *clk_rate = max_clk_rate; | |
+ perf->bw_overlap += bw_overlap_max; | |
+ perf->prefill_bytes += prefill_bytes; | |
+ | |
+ if (max_clk_rate > perf->mdp_clk_rate) | |
+ perf->mdp_clk_rate = max_clk_rate; | |
+ | |
+ pr_debug("final mixer=%d video=%d clk_rate=%u bw=%llu prefill=%d\n", | |
+ mixer->num, mixer->ctl->is_video_mode, perf->mdp_clk_rate, | |
+ perf->bw_overlap, perf->prefill_bytes); | |
- pr_debug("final mixer=%d clk_rate=%u bus ab=%u ib=%u\n", mixer->num, | |
- *clk_rate, *bus_ab_quota, *bus_ib_quota); | |
} | |
-static int mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl) | |
+static u32 mdss_mdp_get_vbp_factor(struct mdss_mdp_ctl *ctl) | |
{ | |
- int ret = MDSS_MDP_PERF_UPDATE_SKIP; | |
- u32 clk_rate, ab_quota, ib_quota; | |
- u32 max_clk_rate = 0, total_ab_quota = 0, total_ib_quota = 0; | |
+ u32 fps, v_total, vbp, vbp_fac; | |
+ struct mdss_panel_info *pinfo; | |
- if (ctl->mixer_left) { | |
- mdss_mdp_perf_mixer_update(ctl->mixer_left, &ab_quota, | |
- &ib_quota, &clk_rate); | |
- total_ab_quota += ab_quota; | |
- total_ib_quota += ib_quota; | |
- max_clk_rate = clk_rate; | |
+ if (!ctl || !ctl->panel_data) | |
+ return 0; | |
+ | |
+ pinfo = &ctl->panel_data->panel_info; | |
+ fps = mdss_panel_get_framerate(pinfo); | |
+ v_total = mdss_panel_get_vtotal(pinfo); | |
+ vbp = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width; | |
+ vbp_fac = (vbp) ? fps * v_total / vbp : 0; | |
+ pr_debug("vbp_fac=%d vbp=%d v_total=%d\n", vbp_fac, vbp, v_total); | |
+ | |
+ return vbp_fac; | |
+} | |
+ | |
+static u32 mdss_mdp_get_vbp_factor_max(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ u32 vbp_max = 0; | |
+ int i; | |
+ struct mdss_data_type *mdata; | |
+ | |
+ if (!ctl || !ctl->mdata) | |
+ return 0; | |
+ | |
+ mdata = ctl->mdata; | |
+ for (i = 0; i < mdata->nctl; i++) { | |
+ struct mdss_mdp_ctl *ctl = mdata->ctl_off + i; | |
+ u32 vbp_fac; | |
+ | |
+ if (ctl->power_on) { | |
+ vbp_fac = mdss_mdp_get_vbp_factor(ctl); | |
+ vbp_max = max(vbp_max, vbp_fac); | |
+ } | |
} | |
- if (ctl->mixer_right) { | |
- mdss_mdp_perf_mixer_update(ctl->mixer_right, &ab_quota, | |
- &ib_quota, &clk_rate); | |
- total_ab_quota += ab_quota; | |
- total_ib_quota += ib_quota; | |
- if (clk_rate > max_clk_rate) | |
- max_clk_rate = clk_rate; | |
+ return vbp_max; | |
+} | |
+ | |
+static bool mdss_mdp_video_mode_intf_connected(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ int i; | |
+ struct mdss_data_type *mdata; | |
+ | |
+ if (!ctl || !ctl->mdata) | |
+ return 0; | |
+ | |
+ mdata = ctl->mdata; | |
+ for (i = 0; i < mdata->nctl; i++) { | |
+ struct mdss_mdp_ctl *ctl = mdata->ctl_off + i; | |
+ | |
+ if (ctl->is_video_mode && ctl->power_on) { | |
+ pr_debug("video interface connected ctl:%d\n", | |
+ ctl->num); | |
+ return true; | |
+ } | |
+ } | |
+ | |
+ return false; | |
+} | |
+ | |
+ | |
+static void __mdss_mdp_perf_calc_ctl_helper(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_perf_params *perf, | |
+ struct mdss_mdp_pipe **left_plist, int left_cnt, | |
+ struct mdss_mdp_pipe **right_plist, int right_cnt) | |
+{ | |
+ struct mdss_mdp_perf_params tmp; | |
+ | |
+ memset(perf, 0, sizeof(*perf)); | |
+ | |
+ if (left_cnt && ctl->mixer_left) { | |
+ mdss_mdp_perf_calc_mixer(ctl->mixer_left, &tmp, | |
+ left_plist, left_cnt); | |
+ perf->bw_overlap += tmp.bw_overlap; | |
+ perf->prefill_bytes += tmp.prefill_bytes; | |
+ perf->mdp_clk_rate = tmp.mdp_clk_rate; | |
+ } | |
+ | |
+ if (right_cnt && ctl->mixer_right) { | |
+ mdss_mdp_perf_calc_mixer(ctl->mixer_right, &tmp, | |
+ right_plist, right_cnt); | |
+ perf->bw_overlap += tmp.bw_overlap; | |
+ perf->prefill_bytes += tmp.prefill_bytes; | |
+ if (tmp.mdp_clk_rate > perf->mdp_clk_rate) | |
+ perf->mdp_clk_rate = tmp.mdp_clk_rate; | |
if (ctl->intf_type) { | |
- clk_rate = mdss_mdp_get_pclk_rate(ctl); | |
+ u32 clk_rate = mdss_mdp_get_pclk_rate(ctl); | |
/* minimum clock rate due to inefficiency in 3dmux */ | |
clk_rate = mult_frac(clk_rate >> 1, 9, 8); | |
- if (clk_rate > max_clk_rate) | |
- max_clk_rate = clk_rate; | |
+ if (clk_rate > perf->mdp_clk_rate) | |
+ perf->mdp_clk_rate = clk_rate; | |
} | |
} | |
/* request minimum bandwidth to have bus clock on when display is on */ | |
- if (total_ib_quota == 0) | |
- total_ib_quota = SZ_16M >> MDSS_MDP_BUS_FACTOR_SHIFT; | |
+ if (perf->bw_overlap == 0) | |
+ perf->bw_overlap = SZ_16M; | |
- if (max_clk_rate != ctl->clk_rate) { | |
- if (max_clk_rate > ctl->clk_rate) | |
- ret = MDSS_MDP_PERF_UPDATE_EARLY; | |
- else | |
- ret = MDSS_MDP_PERF_UPDATE_LATE; | |
- ctl->clk_rate = max_clk_rate; | |
- ctl->perf_changed |= MDSS_MDP_PERF_UPDATE_CLK; | |
+ if (ctl->intf_type != MDSS_MDP_NO_INTF) { | |
+ u32 vbp_fac = mdss_mdp_get_vbp_factor_max(ctl); | |
+ | |
+ perf->bw_prefill = perf->prefill_bytes; | |
+ /* | |
+ * Prefill bandwidth equals the amount of data (number | |
+ * of prefill_bytes) divided by the the amount time | |
+ * available (blanking period). It is equivalent that | |
+ * prefill bytes times a factor in unit Hz, which is | |
+ * the reciprocal of time. | |
+ */ | |
+ perf->bw_prefill *= vbp_fac; | |
} | |
- if ((total_ab_quota != ctl->bus_ab_quota) || | |
- (total_ib_quota != ctl->bus_ib_quota)) { | |
- if (ret == MDSS_MDP_PERF_UPDATE_SKIP) { | |
- if (total_ib_quota >= ctl->bus_ib_quota) | |
- ret = MDSS_MDP_PERF_UPDATE_EARLY; | |
- else | |
- ret = MDSS_MDP_PERF_UPDATE_LATE; | |
+ perf->bw_ctl = max(perf->bw_prefill, perf->bw_overlap); | |
+} | |
+ | |
+int mdss_mdp_perf_bw_check(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_pipe **left_plist, int left_cnt, | |
+ struct mdss_mdp_pipe **right_plist, int right_cnt) | |
+{ | |
+ struct mdss_data_type *mdata = ctl->mdata; | |
+ struct mdss_mdp_perf_params perf; | |
+ u32 bw, threshold; | |
+ | |
+ /* we only need bandwidth check on real-time clients (interfaces) */ | |
+ if (ctl->intf_type == MDSS_MDP_NO_INTF) | |
+ return 0; | |
+ | |
+ __mdss_mdp_perf_calc_ctl_helper(ctl, &perf, | |
+ left_plist, left_cnt, right_plist, right_cnt); | |
+ | |
+ /* convert bandwidth to kb */ | |
+ bw = DIV_ROUND_UP_ULL(perf.bw_ctl, 1000); | |
+ pr_debug("calculated bandwidth=%uk\n", bw); | |
+ | |
+ threshold = ctl->is_video_mode ? mdata->max_bw_low : mdata->max_bw_high; | |
+ if (bw > threshold) { | |
+ pr_debug("exceeds bandwidth: %ukb > %ukb\n", bw, threshold); | |
+ return -E2BIG; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void mdss_mdp_perf_calc_ctl(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_perf_params *perf) | |
+{ | |
+ struct mdss_mdp_pipe **left_plist, **right_plist; | |
+ | |
+ left_plist = ctl->mixer_left ? ctl->mixer_left->stage_pipe : NULL; | |
+ right_plist = ctl->mixer_right ? ctl->mixer_right->stage_pipe : NULL; | |
+ | |
+ __mdss_mdp_perf_calc_ctl_helper(ctl, perf, | |
+ left_plist, (left_plist ? MDSS_MDP_MAX_STAGE : 0), | |
+ right_plist, (right_plist ? MDSS_MDP_MAX_STAGE : 0)); | |
+ | |
+ if (ctl->is_video_mode || ((ctl->intf_type != MDSS_MDP_NO_INTF) && | |
+ mdss_mdp_video_mode_intf_connected(ctl))) { | |
+ perf->bw_ctl = | |
+ max(apply_fudge_factor(perf->bw_overlap, | |
+ &mdss_res->ib_factor_overlap), | |
+ apply_fudge_factor(perf->bw_prefill, | |
+ &mdss_res->ib_factor)); | |
+ } | |
+ pr_debug("ctl=%d clk_rate=%u\n", ctl->num, perf->mdp_clk_rate); | |
+ pr_debug("bw_overlap=%llu bw_prefill=%llu prefill_bytes=%d\n", | |
+ perf->bw_overlap, perf->bw_prefill, perf->prefill_bytes); | |
+} | |
+ | |
+static void set_status(u32 *value, bool status, u32 bit_num) | |
+{ | |
+ if (status) | |
+ *value |= BIT(bit_num); | |
+ else | |
+ *value &= ~BIT(bit_num); | |
+} | |
+ | |
+/** | |
+ * @ mdss_mdp_ctl_perf_set_transaction_status() - | |
+ * Set the status of the on-going operations | |
+ * for the command mode panels. | |
+ * @ctl - pointer to a ctl | |
+ * | |
+ * This function is called to set the status bit in the perf_transaction_status | |
+ * according to the operation that it is on-going for the command mode | |
+ * panels, where: | |
+ * | |
+ * PERF_SW_COMMIT_STATE: | |
+ * 1 - If SW operation has been commited and bw | |
+ * has been requested (HW transaction have not started yet). | |
+ * 0 - If there is no SW operation pending | |
+ * PERF_HW_MDP_STATE: | |
+ * 1 - If HW transaction is on-going | |
+ * 0 - If there is no HW transaction on going (ping-pong interrupt | |
+ * has finished) | |
+ * Only if both states are zero there are no pending operations and | |
+ * BW could be released. | |
+ * State can be queried calling "mdss_mdp_ctl_perf_get_transaction_status" | |
+ */ | |
+void mdss_mdp_ctl_perf_set_transaction_status(struct mdss_mdp_ctl *ctl, | |
+ enum mdss_mdp_perf_state_type component, bool new_status) | |
+{ | |
+ u32 previous_transaction; | |
+ bool previous_status; | |
+ unsigned long flags; | |
+ | |
+ if (!ctl || !ctl->panel_data || | |
+ (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) | |
+ return; | |
+ | |
+ spin_lock_irqsave(&ctl->spin_lock, flags); | |
+ | |
+ previous_transaction = ctl->perf_transaction_status; | |
+ previous_status = previous_transaction & BIT(component) ? | |
+ PERF_STATUS_BUSY : PERF_STATUS_DONE; | |
+ | |
+ /* | |
+ * If we set "done" state when previous state was not "busy", | |
+ * we want to print a warning since maybe there is a state | |
+ * that we are not considering | |
+ */ | |
+ WARN((PERF_STATUS_DONE == new_status) && | |
+ (PERF_STATUS_BUSY != previous_status), | |
+ "unexpected previous state for component: %d\n", component); | |
+ | |
+ set_status(&ctl->perf_transaction_status, new_status, | |
+ (u32)component); | |
+ | |
+ pr_debug("component:%d previous_transaction:%d transaction_status:%d\n", | |
+ component, previous_transaction, ctl->perf_transaction_status); | |
+ pr_debug("new_status:%d prev_status:%d\n", | |
+ new_status, previous_status); | |
+ | |
+ spin_unlock_irqrestore(&ctl->spin_lock, flags); | |
+} | |
+ | |
+/** | |
+ * @ mdss_mdp_ctl_perf_get_transaction_status() - | |
+ * Get the status of the on-going operations | |
+ * for the command mode panels. | |
+ * @ctl - pointer to a ctl | |
+ * | |
+ * Return: | |
+ * The status of the transactions for the command mode panels, | |
+ * note that the bandwidth can be released only if all transaction | |
+ * status bits are zero. | |
+ */ | |
+u32 mdss_mdp_ctl_perf_get_transaction_status(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ unsigned long flags; | |
+ u32 transaction_status; | |
+ | |
+ /* | |
+ * If Video Mode or not valid data to determine the status, return busy | |
+ * status, so the bandwidth cannot be freed by the caller | |
+ */ | |
+ if (!ctl || !ctl->panel_data || | |
+ (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) { | |
+ return PERF_STATUS_BUSY; | |
+ } | |
+ | |
+ spin_lock_irqsave(&ctl->spin_lock, flags); | |
+ transaction_status = ctl->perf_transaction_status; | |
+ spin_unlock_irqrestore(&ctl->spin_lock, flags); | |
+ | |
+ return transaction_status; | |
+} | |
+ | |
+static inline void mdss_mdp_ctl_perf_update_bus(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ u64 bw_sum_of_intfs = 0; | |
+ u64 bus_ab_quota, bus_ib_quota; | |
+ struct mdss_data_type *mdata; | |
+ int i; | |
+ | |
+ if (!ctl || !ctl->mdata) | |
+ return; | |
+ ATRACE_BEGIN(__func__); | |
+ mdata = ctl->mdata; | |
+ for (i = 0; i < mdata->nctl; i++) { | |
+ struct mdss_mdp_ctl *ctl; | |
+ ctl = mdata->ctl_off + i; | |
+ if (ctl->power_on) { | |
+ bw_sum_of_intfs += ctl->cur_perf.bw_ctl; | |
+ pr_debug("c=%d bw=%llu\n", ctl->num, | |
+ ctl->cur_perf.bw_ctl); | |
} | |
- ctl->bus_ab_quota = total_ab_quota; | |
- ctl->bus_ib_quota = total_ib_quota; | |
- ctl->perf_changed |= MDSS_MDP_PERF_UPDATE_BUS; | |
} | |
+ bus_ib_quota = bw_sum_of_intfs; | |
+ bus_ab_quota = apply_fudge_factor(bw_sum_of_intfs, | |
+ &mdss_res->ab_factor); | |
+ trace_mdp_perf_update_bus(bus_ab_quota, bus_ib_quota); | |
+ ATRACE_INT("bus_quota", bus_ib_quota); | |
+ mdss_bus_scale_set_quota(MDSS_HW_MDP, bus_ab_quota, bus_ib_quota); | |
+ pr_debug("ab=%llu ib=%llu\n", bus_ab_quota, bus_ib_quota); | |
+ ATRACE_END(__func__); | |
+} | |
- return ret; | |
+/** | |
+ * @mdss_mdp_ctl_perf_release_bw() - request zero bandwidth | |
+ * @ctl - pointer to a ctl | |
+ * | |
+ * Function checks a state variable for the ctl, if all pending commit | |
+ * requests are done, meaning no more bandwidth is needed, release | |
+ * bandwidth request. | |
+ */ | |
+void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ int transaction_status; | |
+ struct mdss_data_type *mdata; | |
+ int i; | |
+ | |
+ /* only do this for command panel */ | |
+ if (!ctl || !ctl->mdata || !ctl->panel_data || | |
+ (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) | |
+ return; | |
+ | |
+ mutex_lock(&mdss_mdp_ctl_lock); | |
+ mdata = ctl->mdata; | |
+ /* | |
+ * If video interface present, cmd panel bandwidth cannot be | |
+ * released. | |
+ */ | |
+ for (i = 0; i < mdata->nctl; i++) { | |
+ struct mdss_mdp_ctl *ctl = mdata->ctl_off + i; | |
+ | |
+ if (ctl->power_on && ctl->is_video_mode) | |
+ goto exit; | |
+ } | |
+ | |
+ transaction_status = mdss_mdp_ctl_perf_get_transaction_status(ctl); | |
+ pr_debug("transaction_status=0x%x\n", transaction_status); | |
+ | |
+ /*Release the bandwidth only if there are no transactions pending*/ | |
+ if (!transaction_status) { | |
+ trace_mdp_cmd_release_bw(ctl->num); | |
+ ctl->cur_perf.bw_ctl = 0; | |
+ ctl->new_perf.bw_ctl = 0; | |
+ pr_debug("Release BW ctl=%d\n", ctl->num); | |
+ mdss_mdp_ctl_perf_update_bus(ctl); | |
+ } | |
+exit: | |
+ mutex_unlock(&mdss_mdp_ctl_lock); | |
+} | |
+ | |
+static int mdss_mdp_select_clk_lvl(struct mdss_mdp_ctl *ctl, | |
+ u32 clk_rate) | |
+{ | |
+ int i; | |
+ struct mdss_data_type *mdata; | |
+ | |
+ if (!ctl) | |
+ return -ENODEV; | |
+ | |
+ mdata = ctl->mdata; | |
+ | |
+ for (i = 0; i < mdata->nclk_lvl; i++) { | |
+ if (clk_rate > mdata->clock_levels[i]) { | |
+ continue; | |
+ } else { | |
+ clk_rate = mdata->clock_levels[i]; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return clk_rate; | |
+} | |
+ | |
+static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl, | |
+ int params_changed) | |
+{ | |
+ struct mdss_mdp_perf_params *new, *old; | |
+ int update_bus = 0, update_clk = 0; | |
+ struct mdss_data_type *mdata; | |
+ bool is_bw_released; | |
+ | |
+ if (!ctl || !ctl->mdata) | |
+ return; | |
+ ATRACE_BEGIN(__func__); | |
+ mutex_lock(&mdss_mdp_ctl_lock); | |
+ | |
+ mdata = ctl->mdata; | |
+ old = &ctl->cur_perf; | |
+ new = &ctl->new_perf; | |
+ | |
+ /* | |
+ * We could have released the bandwidth if there were no transactions | |
+ * pending, so we want to re-calculate the bandwidth in this situation | |
+ */ | |
+ is_bw_released = !mdss_mdp_ctl_perf_get_transaction_status(ctl); | |
+ | |
+ if (ctl->power_on) { | |
+ if (is_bw_released || params_changed) | |
+ mdss_mdp_perf_calc_ctl(ctl, new); | |
+ /* | |
+ * if params have just changed delay the update until | |
+ * later once the hw configuration has been flushed to | |
+ * MDP | |
+ */ | |
+ if ((params_changed && (new->bw_ctl > old->bw_ctl)) || | |
+ (!params_changed && (new->bw_ctl < old->bw_ctl))) { | |
+ pr_debug("c=%d p=%d new_bw=%llu,old_bw=%llu\n", | |
+ ctl->num, params_changed, new->bw_ctl, | |
+ old->bw_ctl); | |
+ old->bw_ctl = new->bw_ctl; | |
+ update_bus = 1; | |
+ } | |
+ | |
+ if ((params_changed && (new->mdp_clk_rate > old->mdp_clk_rate)) | |
+ || (!params_changed && (new->mdp_clk_rate < | |
+ old->mdp_clk_rate))) { | |
+ old->mdp_clk_rate = new->mdp_clk_rate; | |
+ update_clk = 1; | |
+ } | |
+ } else { | |
+ memset(old, 0, sizeof(old)); | |
+ memset(new, 0, sizeof(new)); | |
+ update_bus = 1; | |
+ update_clk = 1; | |
+ } | |
+ | |
+ if (update_bus) | |
+ mdss_mdp_ctl_perf_update_bus(ctl); | |
+ | |
+ if (update_clk) { | |
+ u32 clk_rate = 0; | |
+ int i; | |
+ | |
+ for (i = 0; i < mdata->nctl; i++) { | |
+ struct mdss_mdp_ctl *ctl; | |
+ ctl = mdata->ctl_off + i; | |
+ if (ctl->power_on) | |
+ clk_rate = max(ctl->cur_perf.mdp_clk_rate, | |
+ clk_rate); | |
+ } | |
+ | |
+ clk_rate = mdss_mdp_select_clk_lvl(ctl, clk_rate); | |
+ ATRACE_INT("mdp_clk", clk_rate); | |
+ mdss_mdp_set_clk_rate(clk_rate); | |
+ pr_debug("update clk rate = %d HZ\n", clk_rate); | |
+ } | |
+ | |
+ mutex_unlock(&mdss_mdp_ctl_lock); | |
+ ATRACE_END(__func__); | |
} | |
static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata, | |
@@ -394,6 +1040,7 @@ static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata, | |
ctl->ref_cnt++; | |
ctl->mdata = mdata; | |
mutex_init(&ctl->lock); | |
+ spin_lock_init(&ctl->spin_lock); | |
BLOCKING_INIT_NOTIFIER_HEAD(&ctl->notifier_head); | |
pr_debug("alloc ctl_num=%d\n", ctl->num); | |
break; | |
@@ -615,13 +1262,17 @@ int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer) | |
mdss_mdp_ctl_free(ctl); | |
- mdss_mdp_ctl_perf_commit(ctl->mdata, MDSS_MDP_PERF_UPDATE_ALL); | |
+ mdss_mdp_ctl_perf_update(ctl, 0); | |
return 0; | |
} | |
int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff) | |
{ | |
+ struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); | |
+ if (sctl) | |
+ sctl->panel_data->panel_info.cont_splash_enabled = 0; | |
+ | |
switch (ctl->panel_data->panel_info.type) { | |
case MIPI_VIDEO_PANEL: | |
case EDP_PANEL: | |
@@ -646,15 +1297,6 @@ static inline int mdss_mdp_set_split_ctl(struct mdss_mdp_ctl *ctl, | |
return 0; | |
} | |
-static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl( | |
- struct mdss_mdp_ctl *ctl) | |
-{ | |
- if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl)) | |
- return ctl->mixer_right->ctl; | |
- | |
- return NULL; | |
-} | |
- | |
static int mdss_mdp_ctl_fbc_enable(int enable, | |
struct mdss_mdp_mixer *mixer, struct mdss_panel_info *pdata) | |
{ | |
@@ -734,7 +1376,7 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) | |
ctl->width = width; | |
ctl->height = height; | |
- ctl->roi = (struct mdss_rect) {0, 0, width, height}; | |
+ ctl->roi = (struct mdss_mdp_img_rect) {0, 0, width, height}; | |
if (!ctl->mixer_left) { | |
ctl->mixer_left = | |
@@ -753,7 +1395,7 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) | |
ctl->mixer_left->width = width; | |
ctl->mixer_left->height = height; | |
- ctl->mixer_left->roi = (struct mdss_rect) {0, 0, width, height}; | |
+ ctl->mixer_left->roi = (struct mdss_mdp_img_rect) {0, 0, width, height}; | |
if (split_ctl) { | |
pr_debug("split display detected\n"); | |
@@ -776,7 +1418,7 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) | |
} | |
ctl->mixer_right->width = width; | |
ctl->mixer_right->height = height; | |
- ctl->mixer_right->roi = (struct mdss_rect) | |
+ ctl->mixer_right->roi = (struct mdss_mdp_img_rect) | |
{0, 0, width, height}; | |
} else if (ctl->mixer_right) { | |
mdss_mdp_mixer_free(ctl->mixer_right); | |
@@ -917,7 +1559,11 @@ struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, | |
switch (pdata->panel_info.bpp) { | |
case 18: | |
- ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666; | |
+ if (ctl->intf_type == MDSS_INTF_DSI) | |
+ ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666 | | |
+ MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB; | |
+ else | |
+ ctl->dst_format = MDSS_MDP_PANEL_FORMAT_RGB666; | |
dither.flags = MDP_PP_OPS_ENABLE | MDP_PP_OPS_WRITE; | |
dither.g_y_depth = 2; | |
dither.r_cr_depth = 2; | |
@@ -984,7 +1630,7 @@ int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl, | |
mixer->width = sctl->width; | |
mixer->height = sctl->height; | |
- mixer->roi = (struct mdss_rect) | |
+ mixer->roi = (struct mdss_mdp_img_rect) | |
{0, 0, mixer->width, mixer->height}; | |
sctl->mixer_left = mixer; | |
@@ -1071,6 +1717,27 @@ int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg) | |
return rc; | |
} | |
+/* | |
+ * mdss_mdp_ctl_restore() - restore mdp ctl path | |
+ * @ctl: mdp controller. | |
+ * | |
+ * This function is called whenever MDP comes out of a power collapse as | |
+ * a result of a screen update when DSI ULPS mode is enabled. It restores | |
+ * the MDP controller's software state to the hardware registers. | |
+ */ | |
+void mdss_mdp_ctl_restore(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ u32 temp; | |
+ | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ temp = readl_relaxed(ctl->mdata->mdp_base + | |
+ MDSS_MDP_REG_DISP_INTF_SEL); | |
+ temp |= (ctl->intf_type << ((ctl->intf_num - MDSS_MDP_INTF0) * 8)); | |
+ writel_relaxed(temp, ctl->mdata->mdp_base + | |
+ MDSS_MDP_REG_DISP_INTF_SEL); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
+} | |
+ | |
static int mdss_mdp_ctl_start_sub(struct mdss_mdp_ctl *ctl, bool handoff) | |
{ | |
struct mdss_mdp_mixer *mixer; | |
@@ -1151,9 +1818,7 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff) | |
if (!handoff) | |
ctl->power_on = true; | |
- ctl->bus_ab_quota = 0; | |
- ctl->bus_ib_quota = 0; | |
- ctl->clk_rate = 0; | |
+ memset(&ctl->cur_perf, 0, sizeof(ctl->cur_perf)); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
@@ -1242,8 +1907,7 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) | |
ctl->power_on = false; | |
ctl->play_cnt = 0; | |
- ctl->clk_rate = 0; | |
- mdss_mdp_ctl_perf_commit(ctl->mdata, MDSS_MDP_PERF_UPDATE_ALL); | |
+ mdss_mdp_ctl_perf_update(ctl, 0); | |
} | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
@@ -1256,12 +1920,12 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) | |
void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl, | |
struct mdp_display_commit *data) | |
{ | |
- struct mdss_rect temp_roi, mixer_roi; | |
+ struct mdss_mdp_img_rect temp_roi, mixer_roi; | |
- temp_roi.x = data->roi.x; | |
- temp_roi.y = data->roi.y; | |
- temp_roi.w = data->roi.w; | |
- temp_roi.h = data->roi.h; | |
+ temp_roi.x = data->roi.x; | |
+ temp_roi.y = data->roi.y; | |
+ temp_roi.w = data->roi.w; | |
+ temp_roi.h = data->roi.h; | |
/* | |
* No Partial Update for: | |
@@ -1271,13 +1935,16 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl, | |
if (!temp_roi.w || !temp_roi.h || ctl->mixer_right || | |
(ctl->panel_data->panel_info.type != MIPI_CMD_PANEL) || | |
!ctl->panel_data->panel_info.partial_update_enabled) { | |
- temp_roi = (struct mdss_rect) | |
+ temp_roi = (struct mdss_mdp_img_rect) | |
{0, 0, ctl->mixer_left->width, | |
ctl->mixer_left->height}; | |
} | |
ctl->roi_changed = 0; | |
- if (!mdss_rect_cmp(&temp_roi, &ctl->roi)) { | |
+ if (((temp_roi.x != ctl->roi.x) || | |
+ (temp_roi.y != ctl->roi.y)) || | |
+ ((temp_roi.w != ctl->roi.w) || | |
+ (temp_roi.h != ctl->roi.h))) { | |
ctl->roi = temp_roi; | |
ctl->roi_changed++; | |
@@ -1337,12 +2004,14 @@ static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl, | |
int stage, secure = 0; | |
int screen_state; | |
int outsize = 0; | |
+ u32 op_mode; | |
screen_state = ctl->force_screen_state; | |
if (!mixer) | |
return -ENODEV; | |
+ trace_mdp_mixer_update(mixer->num); | |
pr_debug("setup mixer=%d\n", mixer->num); | |
outsize = (mixer->roi.h << 16) | mixer->roi.w; | |
@@ -1448,6 +2117,8 @@ static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl, | |
mixercfg |= stage << (3 * pipe->num); | |
+ trace_mdp_sspp_change(pipe); | |
+ | |
pr_debug("stg=%d op=%x fg_alpha=%x bg_alpha=%x\n", stage, | |
blend_op, fg_alpha, bg_alpha); | |
mdp_mixer_write(mixer, off + MDSS_MDP_REG_LM_OP_MODE, blend_op); | |
@@ -1470,6 +2141,11 @@ update_mixer: | |
else | |
ctl->flush_bits |= BIT(6) << mixer->num; | |
+ op_mode = mdp_mixer_read(mixer, MDSS_MDP_REG_LM_OP_MODE); | |
+ /* Read GC enable/disable status on LM */ | |
+ op_mode = (op_mode & BIT(0)); | |
+ blend_color_out |= op_mode; | |
+ | |
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_OP_MODE, blend_color_out); | |
off = __mdss_mdp_ctl_get_mixer_off(mixer); | |
mdss_mdp_ctl_write(ctl, off, mixercfg); | |
@@ -1762,9 +2438,12 @@ static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer) | |
int mdss_mdp_ctl_update_fps(struct mdss_mdp_ctl *ctl, int fps) | |
{ | |
int ret = 0; | |
+ struct mdss_mdp_ctl *sctl = NULL; | |
+ | |
+ sctl = mdss_mdp_get_split_ctl(ctl); | |
if (ctl->config_fps_fnc) | |
- ret = ctl->config_fps_fnc(ctl, fps); | |
+ ret = ctl->config_fps_fnc(ctl, sctl, fps); | |
return ret; | |
} | |
@@ -1852,13 +2531,14 @@ int mdss_mdp_display_wait4comp(struct mdss_mdp_ctl *ctl) | |
return 0; | |
} | |
+ ATRACE_BEGIN("wait_fnc"); | |
if (ctl->wait_fnc) | |
ret = ctl->wait_fnc(ctl, NULL); | |
+ ATRACE_END("wait_fnc"); | |
- if (ctl->perf_changed) { | |
- mdss_mdp_ctl_perf_commit(ctl->mdata, ctl->perf_changed); | |
- ctl->perf_changed = 0; | |
- } | |
+ trace_mdp_commit(ctl); | |
+ | |
+ mdss_mdp_ctl_perf_update(ctl, 0); | |
mutex_unlock(&ctl->lock); | |
@@ -1891,7 +2571,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) | |
struct mdss_mdp_ctl *sctl = NULL; | |
int mixer1_changed, mixer2_changed; | |
int ret = 0; | |
- int perf_update = MDSS_MDP_PERF_UPDATE_SKIP; | |
+ bool is_bw_released; | |
if (!ctl) { | |
pr_err("display function not set\n"); | |
@@ -1912,21 +2592,28 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) | |
mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
- if (mixer1_changed || mixer2_changed | |
- || ctl->force_screen_state) { | |
- perf_update = mdss_mdp_ctl_perf_update(ctl); | |
+ /* | |
+ * We could have released the bandwidth if there were no transactions | |
+ * pending, so we want to re-calculate the bandwidth in this situation | |
+ */ | |
+ is_bw_released = !mdss_mdp_ctl_perf_get_transaction_status(ctl); | |
+ mdss_mdp_ctl_perf_set_transaction_status(ctl, PERF_SW_COMMIT_STATE, | |
+ PERF_STATUS_BUSY); | |
+ | |
+ if (is_bw_released || mixer1_changed || mixer2_changed | |
+ || ctl->force_screen_state) { | |
+ ATRACE_BEGIN("prepare_fnc"); | |
if (ctl->prepare_fnc) | |
ret = ctl->prepare_fnc(ctl, arg); | |
+ ATRACE_END("prepare_fnc"); | |
if (ret) { | |
pr_err("error preparing display\n"); | |
goto done; | |
} | |
- if (perf_update == MDSS_MDP_PERF_UPDATE_EARLY) { | |
- mdss_mdp_ctl_perf_commit(ctl->mdata, ctl->perf_changed); | |
- ctl->perf_changed = 0; | |
- } | |
+ ATRACE_BEGIN("mixer_programming"); | |
+ mdss_mdp_ctl_perf_update(ctl, 1); | |
if (mixer1_changed) | |
mdss_mdp_mixer_update(ctl->mixer_left); | |
@@ -1941,17 +2628,29 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) | |
sctl->opmode); | |
sctl->flush_bits |= BIT(17); | |
} | |
+ ATRACE_END("mixer_programming"); | |
} | |
- mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY); | |
+ ATRACE_BEGIN("frame_ready"); | |
+ if (!ctl->shared_lock) | |
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY); | |
+ ATRACE_END("frame_ready"); | |
+ ATRACE_BEGIN("wait_pingpong"); | |
if (ctl->wait_pingpong) | |
ctl->wait_pingpong(ctl, NULL); | |
+ ATRACE_END("wait_pingpong"); | |
+ ctl->roi_bkup.w = ctl->roi.w; | |
+ ctl->roi_bkup.h = ctl->roi.h; | |
+ | |
+ ATRACE_BEGIN("postproc_programming"); | |
if (ctl->mfd && ctl->mfd->dcm_state != DTM_ENTER) | |
/* postprocessing setup, including dspp */ | |
mdss_mdp_pp_setup_locked(ctl); | |
+ ATRACE_END("postproc_programming"); | |
+ ATRACE_BEGIN("flush_kickoff"); | |
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl->flush_bits); | |
if (sctl) { | |
mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH, | |
@@ -1960,12 +2659,15 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) | |
wmb(); | |
ctl->flush_bits = 0; | |
+ mdss_mdp_xlog_mixer_reg(ctl); | |
+ | |
if (ctl->display_fnc) | |
ret = ctl->display_fnc(ctl, arg); /* kickoff */ | |
if (ret) | |
pr_warn("error displaying frame\n"); | |
ctl->play_cnt++; | |
+ ATRACE_END("flush_kickoff"); | |
done: | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
@@ -2125,3 +2827,18 @@ int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num, | |
return rc; | |
} | |
+ | |
+static void mdss_mdp_xlog_mixer_reg(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ int i, off; | |
+ u32 data[MDSS_MDP_INTF_MAX_LAYERMIXER]; | |
+ | |
+ for (i = 0; i < MDSS_MDP_INTF_MAX_LAYERMIXER; i++) { | |
+ off = MDSS_MDP_REG_CTL_LAYER(i); | |
+ data[i] = mdss_mdp_ctl_read(ctl, off); | |
+ } | |
+ MDSS_XLOG(data[MDSS_MDP_INTF_LAYERMIXER0], | |
+ data[MDSS_MDP_INTF_LAYERMIXER1], | |
+ data[MDSS_MDP_INTF_LAYERMIXER2], | |
+ data[MDSS_MDP_INTF_LAYERMIXER3], off); | |
+} | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_formats.h b/drivers/video/msm/mdss/mdss_mdp_formats.h | |
index a2edf90..dcbff88 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_formats.h | |
+++ b/drivers/video/msm/mdss/mdss_mdp_formats.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -81,6 +81,25 @@ enum { | |
}, \ | |
} | |
+#define FMT_RGB_8888_TILE(fmt, alpha_en, e0, e1, e2, e3) \ | |
+ { \ | |
+ .format = (fmt), \ | |
+ .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, \ | |
+ .unpack_tight = 1, \ | |
+ .unpack_align_msb = 0, \ | |
+ .alpha_enable = (alpha_en), \ | |
+ .unpack_count = 4, \ | |
+ .bpp = 4, \ | |
+ .tile = 1, \ | |
+ .element = { (e0), (e1), (e2), (e3) }, \ | |
+ .bits = { \ | |
+ [C3_ALPHA] = COLOR_8BIT, \ | |
+ [C2_R_Cr] = COLOR_8BIT, \ | |
+ [C0_G_Y] = COLOR_8BIT, \ | |
+ [C1_B_Cb] = COLOR_8BIT, \ | |
+ }, \ | |
+ } | |
+ | |
#define FMT_YUV_COMMON(fmt) \ | |
.format = (fmt), \ | |
.is_yuv = 1, \ | |
@@ -125,6 +144,22 @@ static struct mdss_mdp_format_params mdss_mdp_format_map[] = { | |
FMT_RGB_8888(MDP_RGBX_8888, 0, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA), | |
FMT_RGB_8888(MDP_BGRA_8888, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA), | |
FMT_RGB_8888(MDP_BGRX_8888, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA), | |
+ FMT_RGB_8888_TILE(MDP_RGBA_8888_TILE, 1, C2_R_Cr, C0_G_Y, C1_B_Cb, | |
+ C3_ALPHA), | |
+ FMT_RGB_8888_TILE(MDP_ARGB_8888_TILE, 1, C3_ALPHA, C2_R_Cr, C0_G_Y, | |
+ C1_B_Cb), | |
+ FMT_RGB_8888_TILE(MDP_ABGR_8888_TILE, 1, C3_ALPHA, C1_B_Cb, C0_G_Y, | |
+ C2_R_Cr), | |
+ FMT_RGB_8888_TILE(MDP_BGRA_8888_TILE, 1, C1_B_Cb, C0_G_Y, C2_R_Cr, | |
+ C3_ALPHA), | |
+ FMT_RGB_8888_TILE(MDP_RGBX_8888_TILE, 0, C2_R_Cr, C0_G_Y, C1_B_Cb, | |
+ C3_ALPHA), | |
+ FMT_RGB_8888_TILE(MDP_XRGB_8888_TILE, 0, C3_ALPHA, C2_R_Cr, C0_G_Y, | |
+ C1_B_Cb), | |
+ FMT_RGB_8888_TILE(MDP_XBGR_8888_TILE, 0, C3_ALPHA, C1_B_Cb, C0_G_Y, | |
+ C2_R_Cr), | |
+ FMT_RGB_8888_TILE(MDP_BGRX_8888_TILE, 0, C1_B_Cb, C0_G_Y, C2_R_Cr, | |
+ C3_ALPHA), | |
FMT_YUV_PSEUDO(MDP_Y_CRCB_H1V1, MDSS_MDP_CHROMA_RGB, C2_R_Cr, C1_B_Cb), | |
FMT_YUV_PSEUDO(MDP_Y_CBCR_H1V1, MDSS_MDP_CHROMA_RGB, C1_B_Cb, C2_R_Cr), | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h | |
index 9948738..0b67a6d 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h | |
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -21,9 +21,7 @@ | |
#define ENHIST_LUT_ENTRIES 256 | |
#define HIST_V_SIZE 256 | |
-#define MDSS_MDP_HW_REV_100 0x10000000 | |
-#define MDSS_MDP_HW_REV_102 0x10020000 | |
-#define MDSS_MDP_HW_REV_103 0x10030000 | |
+#define MDSS_MDP_FETCH_CONFIG_RESET_VALUE 0x00000087 | |
#define MDSS_REG_HW_VERSION 0x0 | |
#define MDSS_REG_HW_INTR_STATUS 0x10 | |
@@ -111,7 +109,8 @@ enum mdss_mdp_ctl_index { | |
#define MDSS_MDP_REG_CTL_OFFSET(ctl) (0x00600 + ((ctl) * \ | |
MDSS_MDP_CTL_ADDRESS_OFFSET)) | |
-#define MDSS_MDP_REG_CTL_LAYER(lm) ((lm) * 0x004) | |
+#define MDSS_MDP_REG_CTL_LAYER(lm) \ | |
+ ((lm == 5) ? (0x024) : ((lm) * 0x004)) | |
#define MDSS_MDP_REG_CTL_TOP 0x014 | |
#define MDSS_MDP_REG_CTL_FLUSH 0x018 | |
#define MDSS_MDP_REG_CTL_START 0x01C | |
@@ -380,6 +379,7 @@ enum mdss_mdp_writeback_index { | |
#define MDSS_MDP_MAX_AD_AL 65535 | |
#define MDSS_MDP_MAX_AD_STR 255 | |
+#define MDSS_MDP_AD_BL_SCALE 4095 | |
#define MDSS_MDP_REG_AD_BYPASS 0x000 | |
#define MDSS_MDP_REG_AD_CTRL_0 0x004 | |
@@ -522,6 +522,8 @@ enum mdss_mpd_intf_index { | |
#define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F | |
#define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A | |
+#define MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB BIT(7) | |
+ | |
enum mdss_mdp_pingpong_index { | |
MDSS_MDP_PINGPONG0, | |
MDSS_MDP_PINGPONG1, | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c | |
old mode 100644 | |
new mode 100755 | |
index ae45587..70e2972 | |
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c | |
@@ -15,34 +15,35 @@ | |
#include "mdss_mdp.h" | |
#include "mdss_panel.h" | |
+#include "mdss_debug.h" | |
+#include "mdss_mdp_trace.h" | |
#define VSYNC_EXPIRE_TICK 4 | |
-#define START_THRESHOLD 4 | |
-#define CONTINUE_THRESHOLD 4 | |
- | |
#define MAX_SESSIONS 2 | |
/* wait for at most 2 vsync for lowest refresh rate (24hz) */ | |
#define KOFF_TIMEOUT msecs_to_jiffies(84) | |
-#define STOP_TIMEOUT msecs_to_jiffies(16 * (VSYNC_EXPIRE_TICK + 2)) | |
+#define STOP_TIMEOUT(hz) msecs_to_jiffies((1000 / hz) * (VSYNC_EXPIRE_TICK + 2)) | |
+#define ULPS_ENTER_TIME msecs_to_jiffies(100) | |
struct mdss_mdp_cmd_ctx { | |
struct mdss_mdp_ctl *ctl; | |
u32 pp_num; | |
u8 ref_cnt; | |
- struct completion pp_comp; | |
struct completion stop_comp; | |
+ wait_queue_head_t pp_waitq; | |
struct list_head vsync_handlers; | |
int panel_on; | |
- int koff_cnt; | |
+ atomic_t koff_cnt; | |
int clk_enabled; | |
int vsync_enabled; | |
int rdptr_enabled; | |
struct mutex clk_mtx; | |
spinlock_t clk_lock; | |
struct work_struct clk_work; | |
+ struct delayed_work ulps_work; | |
struct work_struct pp_done_work; | |
atomic_t pp_done_cnt; | |
@@ -53,10 +54,15 @@ struct mdss_mdp_cmd_ctx { | |
u16 start_threshold; | |
u32 vclk_line; /* vsync clock per line */ | |
struct mdss_panel_recovery recovery; | |
+ bool ulps; | |
+ struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */ | |
+ u32 pp_timeout_report_cnt; | |
}; | |
struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS]; | |
+static int mdss_mdp_cmd_do_notifier(struct mdss_mdp_cmd_ctx *ctx); | |
+ | |
static inline u32 mdss_mdp_cmd_line_count(struct mdss_mdp_ctl *ctl) | |
{ | |
struct mdss_mdp_mixer *mixer; | |
@@ -101,108 +107,124 @@ exit: | |
return cnt; | |
} | |
-/* | |
- * TE configuration: | |
- * dsi byte clock calculated base on 70 fps | |
- * around 14 ms to complete a kickoff cycle if te disabled | |
- * vclk_line base on 60 fps | |
- * write is faster than read | |
- * init == start == rdptr | |
- */ | |
-static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer, | |
- struct mdss_mdp_cmd_ctx *ctx, int enable) | |
-{ | |
- u32 cfg; | |
- | |
- cfg = BIT(19); /* VSYNC_COUNTER_EN */ | |
- if (ctx->tear_check) | |
- cfg |= BIT(20); /* VSYNC_IN_EN */ | |
- cfg |= ctx->vclk_line; | |
- | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT, | |
- 0xfff0); /* set to verh height */ | |
- | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_VSYNC_INIT_VAL, | |
- ctx->height); | |
- | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_RD_PTR_IRQ, | |
- ctx->height + 1); | |
- | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_START_POS, | |
- ctx->height); | |
- | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_THRESH, | |
- (CONTINUE_THRESHOLD << 16) | (ctx->start_threshold)); | |
- | |
- mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_TEAR_CHECK_EN, enable); | |
- return 0; | |
-} | |
-static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl, int enable) | |
+static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_mixer *mixer) | |
{ | |
- struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; | |
+ struct mdss_mdp_pp_tear_check *te; | |
struct mdss_panel_info *pinfo; | |
- struct mdss_mdp_mixer *mixer; | |
+ u32 vsync_clk_speed_hz, total_lines, vclks_line, cfg; | |
- pinfo = &ctl->panel_data->panel_info; | |
+ if (IS_ERR_OR_NULL(ctl->panel_data)) { | |
+ pr_err("no panel data\n"); | |
+ return -ENODEV; | |
+ } | |
- if (pinfo->mipi.vsync_enable && enable) { | |
- u32 mdp_vsync_clk_speed_hz, total_lines; | |
+ pinfo = &ctl->panel_data->panel_info; | |
+ te = &ctl->panel_data->panel_info.te; | |
- mdss_mdp_vsync_clk_enable(1); | |
+ mdss_mdp_vsync_clk_enable(1); | |
- mdp_vsync_clk_speed_hz = | |
+ vsync_clk_speed_hz = | |
mdss_mdp_get_clk_rate(MDSS_CLK_MDP_VSYNC); | |
- pr_debug("%s: vsync_clk_rate=%d\n", __func__, | |
- mdp_vsync_clk_speed_hz); | |
- if (mdp_vsync_clk_speed_hz == 0) { | |
- pr_err("can't get clk speed\n"); | |
- return -EINVAL; | |
- } | |
+ total_lines = mdss_panel_get_vtotal(pinfo); | |
- ctx->tear_check = pinfo->mipi.hw_vsync_mode; | |
- ctx->height = pinfo->yres; | |
- ctx->vporch = pinfo->lcdc.v_back_porch + | |
- pinfo->lcdc.v_front_porch + | |
- pinfo->lcdc.v_pulse_width; | |
+ total_lines *= pinfo->mipi.frame_rate; | |
- ctx->start_threshold = START_THRESHOLD; | |
+ vclks_line = (total_lines) ? vsync_clk_speed_hz / total_lines : 0; | |
- total_lines = ctx->height + ctx->vporch; | |
- total_lines *= pinfo->mipi.frame_rate; | |
- ctx->vclk_line = mdp_vsync_clk_speed_hz / total_lines; | |
+ cfg = BIT(19); | |
+ if (pinfo->mipi.hw_vsync_mode) | |
+ cfg |= BIT(20); | |
- pr_debug("%s: fr=%d tline=%d vcnt=%d thold=%d vrate=%d\n", | |
- __func__, pinfo->mipi.frame_rate, total_lines, | |
- ctx->vclk_line, ctx->start_threshold, | |
- mdp_vsync_clk_speed_hz); | |
- } else { | |
- enable = 0; | |
+ if (te->refx100) | |
+ vclks_line = vclks_line * pinfo->mipi.frame_rate * | |
+ 100 / te->refx100; | |
+ else { | |
+ pr_warn("refx100 cannot be zero! Use 6000 as default\n"); | |
+ vclks_line = vclks_line * pinfo->mipi.frame_rate * | |
+ 100 / 6000; | |
} | |
- mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); | |
- if (mixer) | |
- mdss_mdp_cmd_tearcheck_cfg(mixer, ctx, enable); | |
+ cfg |= vclks_line; | |
- mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT); | |
- if (mixer) | |
- mdss_mdp_cmd_tearcheck_cfg(mixer, ctx, enable); | |
+ pr_debug("%s: yres=%d vclks=%x height=%d init=%d rd=%d start=%d ", | |
+ __func__, pinfo->yres, vclks_line, te->sync_cfg_height, | |
+ te->vsync_init_val, te->rd_ptr_irq, te->start_pos); | |
+ pr_debug("thrd_start =%d thrd_cont=%d\n", | |
+ te->sync_threshold_start, te->sync_threshold_continue); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT, | |
+ te->sync_cfg_height); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_VSYNC_INIT_VAL, | |
+ te->vsync_init_val); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_RD_PTR_IRQ, | |
+ te->rd_ptr_irq); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_START_POS, | |
+ te->start_pos); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_SYNC_THRESH, | |
+ ((te->sync_threshold_continue << 16) | | |
+ te->sync_threshold_start)); | |
+ mdss_mdp_pingpong_write(mixer, MDSS_MDP_REG_PP_TEAR_CHECK_EN, | |
+ te->tear_check_en); | |
return 0; | |
} | |
+static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl) | |
+{ | |
+ struct mdss_mdp_mixer *mixer; | |
+ int rc = 0; | |
+ mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); | |
+ if (mixer) { | |
+ rc = mdss_mdp_cmd_tearcheck_cfg(ctl, mixer); | |
+ if (rc) | |
+ goto err; | |
+ } | |
+ mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT); | |
+ if (mixer) | |
+ rc = mdss_mdp_cmd_tearcheck_cfg(ctl, mixer); | |
+ err: | |
+ return rc; | |
+} | |
+ | |
static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx) | |
{ | |
unsigned long flags; | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ int rc; | |
+ | |
+ if (!ctx->panel_on) | |
+ return; | |
+ | |
mutex_lock(&ctx->clk_mtx); | |
+ MDSS_XLOG(ctx->pp_num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
if (!ctx->clk_enabled) { | |
+ mdss_bus_bandwidth_ctrl(true); | |
+ | |
ctx->clk_enabled = 1; | |
+ if (cancel_delayed_work_sync(&ctx->ulps_work)) | |
+ pr_debug("deleted pending ulps work\n"); | |
+ | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) | |
+ pr_err("IOMMU attach failed\n"); | |
+ | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ | |
+ if (ctx->ulps) { | |
+ if (mdss_mdp_cmd_tearcheck_setup(ctx->ctl)) | |
+ pr_warn("tearcheck setup failed\n"); | |
+ mdss_mdp_ctl_intf_event(ctx->ctl, | |
+ MDSS_EVENT_DSI_ULPS_CTRL, (void *)0); | |
+ ctx->ulps = false; | |
+ } | |
+ | |
mdss_mdp_ctl_intf_event | |
(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1); | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ | |
mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME); | |
} | |
spin_lock_irqsave(&ctx->clk_lock, flags); | |
@@ -220,6 +242,8 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) | |
int set_clk_off = 0; | |
mutex_lock(&ctx->clk_mtx); | |
+ MDSS_XLOG(ctx->pp_num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
spin_lock_irqsave(&ctx->clk_lock, flags); | |
if (!ctx->rdptr_enabled) | |
set_clk_off = 1; | |
@@ -230,7 +254,11 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) | |
mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_SUSPEND); | |
mdss_mdp_ctl_intf_event | |
(ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0); | |
+ mdss_iommu_ctrl(0); | |
+ mdss_bus_bandwidth_ctrl(false); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
+ if (ctx->panel_on) | |
+ schedule_delayed_work(&ctx->ulps_work, ULPS_ENTER_TIME); | |
} | |
mutex_unlock(&ctx->clk_mtx); | |
} | |
@@ -249,6 +277,8 @@ static void mdss_mdp_cmd_readptr_done(void *arg) | |
vsync_time = ktime_get(); | |
ctl->vsync_cnt++; | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
spin_lock(&ctx->clk_lock); | |
list_for_each_entry(tmp, &ctx->vsync_handlers, list) { | |
@@ -259,6 +289,10 @@ static void mdss_mdp_cmd_readptr_done(void *arg) | |
if (!ctx->vsync_enabled) { | |
if (ctx->rdptr_enabled) | |
ctx->rdptr_enabled--; | |
+ | |
+ /* keep clk on during kickoff */ | |
+ if (ctx->rdptr_enabled == 0 && atomic_read(&ctx->koff_cnt)) | |
+ ctx->rdptr_enabled++; | |
} | |
if (ctx->rdptr_enabled == 0) { | |
@@ -284,14 +318,13 @@ static void mdss_mdp_cmd_underflow_recovery(void *data) | |
if (!ctx->ctl) | |
return; | |
spin_lock_irqsave(&ctx->clk_lock, flags); | |
- if (ctx->koff_cnt) { | |
+ if (atomic_read(&ctx->koff_cnt)) { | |
mdss_mdp_ctl_reset(ctx->ctl); | |
pr_debug("%s: intf_num=%d\n", __func__, | |
ctx->ctl->intf_num); | |
- ctx->koff_cnt--; | |
+ atomic_dec(&ctx->koff_cnt); | |
mdss_mdp_irq_disable_nosync(MDSS_MDP_IRQ_PING_PONG_COMP, | |
ctx->pp_num); | |
- complete_all(&ctx->pp_comp); | |
} | |
spin_unlock_irqrestore(&ctx->clk_lock, flags); | |
} | |
@@ -308,6 +341,9 @@ static void mdss_mdp_cmd_pingpong_done(void *arg) | |
return; | |
} | |
+ mdss_mdp_ctl_perf_set_transaction_status(ctl, | |
+ PERF_HW_MDP_STATE, PERF_STATUS_DONE); | |
+ | |
spin_lock(&ctx->clk_lock); | |
list_for_each_entry(tmp, &ctx->vsync_handlers, list) { | |
if (tmp->enabled && tmp->cmd_post_flush) | |
@@ -315,22 +351,27 @@ static void mdss_mdp_cmd_pingpong_done(void *arg) | |
} | |
mdss_mdp_irq_disable_nosync(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num); | |
- complete_all(&ctx->pp_comp); | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
- if (ctx->koff_cnt) { | |
- atomic_inc(&ctx->pp_done_cnt); | |
- schedule_work(&ctx->pp_done_work); | |
- ctx->koff_cnt--; | |
- if (ctx->koff_cnt) { | |
+ if (atomic_add_unless(&ctx->koff_cnt, -1, 0)) { | |
+ if (atomic_read(&ctx->koff_cnt)) | |
pr_err("%s: too many kickoffs=%d!\n", __func__, | |
- ctx->koff_cnt); | |
- ctx->koff_cnt = 0; | |
+ atomic_read(&ctx->koff_cnt)); | |
+ if (mdss_mdp_cmd_do_notifier(ctx)) { | |
+ atomic_inc(&ctx->pp_done_cnt); | |
+ schedule_work(&ctx->pp_done_work); | |
} | |
- } else | |
+ wake_up_all(&ctx->pp_waitq); | |
+ } else { | |
pr_err("%s: should not have pingpong interrupt!\n", __func__); | |
+ } | |
+ trace_mdp_cmd_pingpong_done(ctl, ctx->pp_num, | |
+ atomic_read(&ctx->koff_cnt)); | |
pr_debug("%s: ctl_num=%d intf_num=%d ctx=%d kcnt=%d\n", __func__, | |
- ctl->num, ctl->intf_num, ctx->pp_num, ctx->koff_cnt); | |
+ ctl->num, ctl->intf_num, ctx->pp_num, | |
+ atomic_read(&ctx->koff_cnt)); | |
spin_unlock(&ctx->clk_lock); | |
} | |
@@ -340,9 +381,12 @@ static void pingpong_done_work(struct work_struct *work) | |
struct mdss_mdp_cmd_ctx *ctx = | |
container_of(work, typeof(*ctx), pp_done_work); | |
- if (ctx->ctl) | |
+ if (ctx->ctl) { | |
while (atomic_add_unless(&ctx->pp_done_cnt, -1, 0)) | |
mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_DONE); | |
+ | |
+ mdss_mdp_ctl_perf_release_bw(ctx->ctl); | |
+ } | |
} | |
static void clk_ctrl_work(struct work_struct *work) | |
@@ -358,11 +402,36 @@ static void clk_ctrl_work(struct work_struct *work) | |
mdss_mdp_cmd_clk_off(ctx); | |
} | |
+static void __mdss_mdp_cmd_ulps_work(struct work_struct *work) | |
+{ | |
+ struct delayed_work *dw = to_delayed_work(work); | |
+ struct mdss_mdp_cmd_ctx *ctx = | |
+ container_of(dw, struct mdss_mdp_cmd_ctx, ulps_work); | |
+ | |
+ if (!ctx) { | |
+ pr_err("%s: invalid ctx\n", __func__); | |
+ return; | |
+ } | |
+ | |
+ if (!ctx->panel_on) { | |
+ pr_err("Panel is off. skipping ULPS configuration\n"); | |
+ return; | |
+ } | |
+ | |
+ if (!mdss_mdp_ctl_intf_event(ctx->ctl, MDSS_EVENT_DSI_ULPS_CTRL, | |
+ (void *)1)) { | |
+ ctx->ulps = true; | |
+ ctx->ctl->play_cnt = 0; | |
+ mdss_mdp_footswitch_ctrl_ulps(0, &ctx->ctl->mfd->pdev->dev); | |
+ } | |
+} | |
+ | |
static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl, | |
struct mdss_mdp_vsync_handler *handle) | |
{ | |
struct mdss_mdp_cmd_ctx *ctx; | |
unsigned long flags; | |
+ bool enable_rdptr = false; | |
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; | |
if (!ctx) { | |
@@ -370,16 +439,21 @@ static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl, | |
return -ENODEV; | |
} | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
+ | |
spin_lock_irqsave(&ctx->clk_lock, flags); | |
if (!handle->enabled) { | |
handle->enabled = true; | |
list_add(&handle->list, &ctx->vsync_handlers); | |
- if (!handle->cmd_post_flush) | |
- ctx->vsync_enabled = 1; | |
+ | |
+ enable_rdptr = !handle->cmd_post_flush; | |
+ if (enable_rdptr) | |
+ ctx->vsync_enabled++; | |
} | |
spin_unlock_irqrestore(&ctx->clk_lock, flags); | |
- if (!handle->cmd_post_flush) | |
+ if (enable_rdptr) | |
mdss_mdp_cmd_clk_on(ctx); | |
return 0; | |
@@ -388,11 +462,8 @@ static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl, | |
static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl, | |
struct mdss_mdp_vsync_handler *handle) | |
{ | |
- | |
struct mdss_mdp_cmd_ctx *ctx; | |
unsigned long flags; | |
- struct mdss_mdp_vsync_handler *tmp; | |
- int num_rdptr_vsync = 0; | |
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; | |
if (!ctx) { | |
@@ -400,19 +471,20 @@ static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl, | |
return -ENODEV; | |
} | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled, 0x88888); | |
spin_lock_irqsave(&ctx->clk_lock, flags); | |
if (handle->enabled) { | |
handle->enabled = false; | |
list_del_init(&handle->list); | |
- } | |
- list_for_each_entry(tmp, &ctx->vsync_handlers, list) { | |
- if (!tmp->cmd_post_flush) | |
- num_rdptr_vsync++; | |
- } | |
- if (!num_rdptr_vsync) { | |
- ctx->vsync_enabled = 0; | |
- ctx->rdptr_enabled = VSYNC_EXPIRE_TICK; | |
+ | |
+ if (!handle->cmd_post_flush) { | |
+ if (ctx->vsync_enabled) | |
+ ctx->vsync_enabled--; | |
+ else | |
+ WARN(1, "unbalanced vsync disable"); | |
+ } | |
} | |
spin_unlock_irqrestore(&ctx->clk_lock, flags); | |
return 0; | |
@@ -428,7 +500,6 @@ int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, bool handoff) | |
pdata->panel_info.cont_splash_enabled = 0; | |
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0); | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
return ret; | |
} | |
@@ -436,8 +507,8 @@ int mdss_mdp_cmd_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, bool handoff) | |
static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) | |
{ | |
struct mdss_mdp_cmd_ctx *ctx; | |
+ struct mdss_panel_data *pdata; | |
unsigned long flags; | |
- int need_wait = 0; | |
int rc = 0; | |
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; | |
@@ -446,31 +517,104 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) | |
return -ENODEV; | |
} | |
- spin_lock_irqsave(&ctx->clk_lock, flags); | |
- if (ctx->koff_cnt > 0) | |
- need_wait = 1; | |
- spin_unlock_irqrestore(&ctx->clk_lock, flags); | |
+ pdata = ctl->panel_data; | |
- pr_debug("%s: need_wait=%d intf_num=%d ctx=%p\n", | |
- __func__, need_wait, ctl->intf_num, ctx); | |
+ ctl->roi_bkup.w = ctl->width; | |
+ ctl->roi_bkup.h = ctl->height; | |
+ | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled, ctl->roi_bkup.w, | |
+ ctl->roi_bkup.h); | |
+ | |
+ pr_debug("%s: intf_num=%d ctx=%p koff_cnt=%d\n", __func__, | |
+ ctl->intf_num, ctx, atomic_read(&ctx->koff_cnt)); | |
+ | |
+ rc = wait_event_timeout(ctx->pp_waitq, | |
+ atomic_read(&ctx->koff_cnt) == 0, | |
+ KOFF_TIMEOUT); | |
+ | |
+ if (rc <= 0) { | |
+ u32 status, mask; | |
+ | |
+ mask = BIT(MDSS_MDP_IRQ_PING_PONG_COMP + ctx->pp_num); | |
+ status = mask & readl_relaxed(ctl->mdata->mdp_base + | |
+ MDSS_MDP_REG_INTR_STATUS); | |
+ if (status) { | |
+ WARN(1, "pp done but irq not triggered\n"); | |
+ mdss_mdp_irq_clear(ctl->mdata, | |
+ MDSS_MDP_IRQ_PING_PONG_COMP, | |
+ ctx->pp_num); | |
+ local_irq_save(flags); | |
+ mdss_mdp_cmd_pingpong_done(ctl); | |
+ local_irq_restore(flags); | |
+ rc = 1; | |
+ } | |
- if (need_wait) { | |
- rc = wait_for_completion_timeout( | |
- &ctx->pp_comp, KOFF_TIMEOUT); | |
+ rc = atomic_read(&ctx->koff_cnt) == 0; | |
+ } | |
- if (rc <= 0) { | |
+ if (rc <= 0) { | |
+ if (!ctx->pp_timeout_report_cnt) { | |
WARN(1, "cmd kickoff timed out (%d) ctl=%d\n", | |
- rc, ctl->num); | |
- rc = -EPERM; | |
- mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_TIMEOUT); | |
- } else { | |
- rc = 0; | |
+ rc, ctl->num); | |
+ mdss_dsi_debug_check_te(pdata); | |
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", | |
+ "edp", "hdmi", "panic"); | |
} | |
+ ctx->pp_timeout_report_cnt++; | |
+ rc = -EPERM; | |
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_TIMEOUT); | |
+ atomic_add_unless(&ctx->koff_cnt, -1, 0); | |
+ } else { | |
+ rc = 0; | |
+ ctx->pp_timeout_report_cnt = 0; | |
} | |
+ /* signal any pending ping pong done events */ | |
+ while (atomic_add_unless(&ctx->pp_done_cnt, -1, 0)) | |
+ mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_DONE); | |
+ | |
+ MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, | |
+ ctx->rdptr_enabled, rc); | |
+ | |
return rc; | |
} | |
+static int mdss_mdp_cmd_do_notifier(struct mdss_mdp_cmd_ctx *ctx) | |
+{ | |
+ struct mdss_mdp_cmd_ctx *sctx; | |
+ sctx = ctx->sync_ctx; | |
+ | |
+ if (!sctx || atomic_read(&sctx->koff_cnt) == 0) | |
+ return 1; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void mdss_mdp_cmd_set_sync_ctx( | |
+ struct mdss_mdp_ctl *ctl, struct mdss_mdp_ctl *sctl) | |
+{ | |
+ struct mdss_mdp_cmd_ctx *ctx, *sctx; | |
+ | |
+ ctx = (struct mdss_mdp_cmd_ctx *)ctl->priv_data; | |
+ if (!sctl) { | |
+ ctx->sync_ctx = NULL; | |
+ return; | |
+ } | |
+ | |
+ sctx = (struct mdss_mdp_cmd_ctx *)sctl->priv_data; | |
+ | |
+ if (!sctl->roi.w && !sctl->roi.h) { | |
+ /* left only */ | |
+ ctx->sync_ctx = NULL; | |
+ sctx->sync_ctx = NULL; | |
+ } else { | |
+ /* left + right */ | |
+ ctx->sync_ctx = sctx; | |
+ sctx->sync_ctx = ctx; | |
+ } | |
+} | |
+ | |
static int mdss_mdp_cmd_set_partial_roi(struct mdss_mdp_ctl *ctl) | |
{ | |
int rc = 0; | |
@@ -489,8 +633,7 @@ static int mdss_mdp_cmd_set_partial_roi(struct mdss_mdp_ctl *ctl) | |
int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) | |
{ | |
- struct mdss_mdp_cmd_ctx *ctx; | |
- unsigned long flags; | |
+ struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; | |
int rc; | |
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; | |
@@ -499,6 +642,9 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) | |
return -ENODEV; | |
} | |
+ mdss_mdp_ctl_perf_set_transaction_status(ctl, | |
+ PERF_HW_MDP_STATE, PERF_STATUS_BUSY); | |
+ | |
if (ctx->panel_on == 0) { | |
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL); | |
WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc); | |
@@ -507,25 +653,39 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) | |
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_ON, NULL); | |
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc); | |
+ | |
+ mdss_mdp_ctl_intf_event(ctl, | |
+ MDSS_EVENT_REGISTER_RECOVERY_HANDLER, | |
+ (void *)&ctx->recovery); | |
} | |
- mdss_mdp_cmd_set_partial_roi(ctl); | |
+ MDSS_XLOG(ctl->num, ctl->roi.x, ctl->roi.y, ctl->roi.w, | |
+ ctl->roi.h); | |
+ | |
+ atomic_inc(&ctx->koff_cnt); | |
+ if (sctx) | |
+ atomic_inc(&sctx->koff_cnt); | |
+ | |
+ trace_mdp_cmd_kickoff(ctl->num, atomic_read(&ctx->koff_cnt)); | |
mdss_mdp_cmd_clk_on(ctx); | |
+ mdss_mdp_cmd_set_partial_roi(ctl); | |
+ | |
/* | |
* tx dcs command if had any | |
*/ | |
- mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF, | |
- (void *)&ctx->recovery); | |
+ mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_CMDLIST_KOFF, NULL); | |
+ | |
+ mdss_mdp_cmd_set_sync_ctx(ctl, NULL); | |
- INIT_COMPLETION(ctx->pp_comp); | |
mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num); | |
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1); | |
- spin_lock_irqsave(&ctx->clk_lock, flags); | |
- ctx->koff_cnt++; | |
- spin_unlock_irqrestore(&ctx->clk_lock, flags); | |
+ mdss_mdp_ctl_perf_set_transaction_status(ctl, | |
+ PERF_SW_COMMIT_STATE, PERF_STATUS_DONE); | |
mb(); | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
return 0; | |
} | |
@@ -538,6 +698,7 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl) | |
struct mdss_mdp_vsync_handler *tmp, *handle; | |
int need_wait = 0; | |
int ret = 0; | |
+ int hz; | |
ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; | |
if (!ctx) { | |
@@ -547,6 +708,8 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl) | |
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list) | |
mdss_mdp_cmd_remove_vsync_handler(ctl, handle); | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled, XLOG_FUNC_ENTRY); | |
spin_lock_irqsave(&ctx->clk_lock, flags); | |
if (ctx->rdptr_enabled) { | |
@@ -555,8 +718,11 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl) | |
} | |
spin_unlock_irqrestore(&ctx->clk_lock, flags); | |
+ hz = mdss_panel_get_framerate(&ctl->panel_data->panel_info); | |
+ | |
if (need_wait) | |
- if (wait_for_completion_timeout(&ctx->stop_comp, STOP_TIMEOUT) | |
+ if (wait_for_completion_timeout(&ctx->stop_comp, | |
+ STOP_TIMEOUT(hz)) | |
<= 0) { | |
WARN(1, "stop cmd time out\n"); | |
@@ -564,24 +730,28 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl) | |
pr_err("no panel data\n"); | |
} else { | |
pinfo = &ctl->panel_data->panel_info; | |
- | |
- if (pinfo->panel_dead) { | |
- mdss_mdp_irq_disable | |
- (MDSS_MDP_IRQ_PING_PONG_RD_PTR, | |
- ctx->pp_num); | |
- ctx->rdptr_enabled = 0; | |
- } | |
+ mdss_mdp_irq_disable | |
+ (MDSS_MDP_IRQ_PING_PONG_RD_PTR, | |
+ ctx->pp_num); | |
+ ctx->rdptr_enabled = 0; | |
} | |
} | |
if (cancel_work_sync(&ctx->clk_work)) | |
pr_debug("no pending clk work\n"); | |
+ if (cancel_delayed_work_sync(&ctx->ulps_work)) | |
+ pr_debug("deleted pending ulps work\n"); | |
+ | |
+ mdss_mdp_ctl_intf_event(ctl, | |
+ MDSS_EVENT_REGISTER_RECOVERY_HANDLER, | |
+ NULL); | |
+ | |
+ ctx->panel_on = 0; | |
mdss_mdp_cmd_clk_off(ctx); | |
flush_work(&ctx->pp_done_work); | |
- ctx->panel_on = 0; | |
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num, | |
NULL, NULL); | |
@@ -603,6 +773,8 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl) | |
ctl->add_vsync_handler = NULL; | |
ctl->remove_vsync_handler = NULL; | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled, XLOG_FUNC_EXIT); | |
pr_debug("%s:-\n", __func__); | |
return 0; | |
@@ -642,11 +814,13 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) | |
ctx->ctl = ctl; | |
ctx->pp_num = mixer->num; | |
- init_completion(&ctx->pp_comp); | |
+ ctx->pp_timeout_report_cnt = 0; | |
+ init_waitqueue_head(&ctx->pp_waitq); | |
init_completion(&ctx->stop_comp); | |
spin_lock_init(&ctx->clk_lock); | |
mutex_init(&ctx->clk_mtx); | |
INIT_WORK(&ctx->clk_work, clk_ctrl_work); | |
+ INIT_DELAYED_WORK(&ctx->ulps_work, __mdss_mdp_cmd_ulps_work); | |
INIT_WORK(&ctx->pp_done_work, pingpong_done_work); | |
atomic_set(&ctx->pp_done_cnt, 0); | |
INIT_LIST_HEAD(&ctx->vsync_handlers); | |
@@ -656,6 +830,8 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) | |
pr_debug("%s: ctx=%p num=%d mixer=%d\n", __func__, | |
ctx, ctx->pp_num, mixer->num); | |
+ MDSS_XLOG(ctl->num, ctx->koff_cnt, ctx->clk_enabled, | |
+ ctx->rdptr_enabled); | |
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num, | |
mdss_mdp_cmd_readptr_done, ctl); | |
@@ -663,7 +839,8 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) | |
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num, | |
mdss_mdp_cmd_pingpong_done, ctl); | |
- ret = mdss_mdp_cmd_tearcheck_setup(ctl, 1); | |
+ ret = mdss_mdp_cmd_tearcheck_setup(ctl); | |
+ | |
if (ret) { | |
pr_err("tearcheck setup failed\n"); | |
return ret; | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c | |
index f8c59d7..6642544 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -16,12 +16,13 @@ | |
#include <linux/iopoll.h> | |
#include <linux/delay.h> | |
#include <linux/dma-mapping.h> | |
-#include <linux/bootmem.h> | |
#include <linux/memblock.h> | |
#include "mdss_fb.h" | |
#include "mdss_mdp.h" | |
#include "mdss_panel.h" | |
+#include "mdss_debug.h" | |
+#include "mdss_mdp_trace.h" | |
/* wait for at least 2 vsyncs for lowest refresh rate (24hz) */ | |
#define VSYNC_TIMEOUT_US 100000 | |
@@ -256,6 +257,8 @@ static int mdss_mdp_video_add_vsync_handler(struct mdss_mdp_ctl *ctl, | |
goto exit; | |
} | |
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt, handle->enabled); | |
+ | |
spin_lock_irqsave(&ctx->vsync_lock, flags); | |
if (!handle->enabled) { | |
handle->enabled = true; | |
@@ -282,6 +285,8 @@ static int mdss_mdp_video_remove_vsync_handler(struct mdss_mdp_ctl *ctl, | |
return -ENODEV; | |
} | |
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt, handle->enabled); | |
+ | |
spin_lock_irqsave(&ctx->vsync_lock, flags); | |
if (handle->enabled) { | |
handle->enabled = false; | |
@@ -298,6 +303,7 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl) | |
{ | |
struct mdss_mdp_video_ctx *ctx; | |
struct mdss_mdp_vsync_handler *tmp, *handle; | |
+ struct mdss_mdp_ctl *sctl; | |
int rc; | |
u32 frame_rate = 0; | |
@@ -308,7 +314,7 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl) | |
pr_err("invalid ctx for ctl=%d\n", ctl->num); | |
return -ENODEV; | |
} | |
- | |
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt); | |
if (ctx->timegen_en) { | |
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, NULL); | |
if (rc == -EBUSY) { | |
@@ -327,6 +333,8 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl) | |
frame_rate = 24; | |
msleep((1000/frame_rate) + 1); | |
} | |
+ | |
+ mdss_iommu_ctrl(0); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
ctx->timegen_en = false; | |
@@ -335,6 +343,12 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl) | |
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN, | |
ctl->intf_num); | |
+ sctl = mdss_mdp_get_split_ctl(ctl); | |
+ if (sctl) | |
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN, | |
+ sctl->intf_num); | |
+ | |
+ mdss_bus_bandwidth_ctrl(false); | |
} | |
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list) | |
@@ -367,6 +381,8 @@ static void mdss_mdp_video_vsync_intr_done(void *arg) | |
vsync_time = ktime_get(); | |
ctl->vsync_cnt++; | |
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt, ctl->vsync_cnt); | |
+ | |
pr_debug("intr ctl=%d vsync cnt=%u vsync_time=%d\n", | |
ctl->num, ctl->vsync_cnt, (int)ktime_to_ms(vsync_time)); | |
@@ -396,6 +412,7 @@ static int mdss_mdp_video_pollwait(struct mdss_mdp_ctl *ctl) | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
if (rc == 0) { | |
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt); | |
pr_debug("vsync poll successful! rc=%d status=0x%x\n", | |
rc, status); | |
ctx->poll_cnt++; | |
@@ -433,8 +450,10 @@ static int mdss_mdp_video_wait4comp(struct mdss_mdp_ctl *ctl, void *arg) | |
if (ctx->polling_en) { | |
rc = mdss_mdp_video_pollwait(ctl); | |
} else { | |
+ mutex_unlock(&ctl->lock); | |
rc = wait_for_completion_timeout(&ctx->vsync_comp, | |
usecs_to_jiffies(VSYNC_TIMEOUT_US)); | |
+ mutex_lock(&ctl->lock); | |
if (rc == 0) { | |
pr_warn("vsync wait timeout %d, fallback to poll mode\n", | |
ctl->num); | |
@@ -455,6 +474,21 @@ static int mdss_mdp_video_wait4comp(struct mdss_mdp_ctl *ctl, void *arg) | |
return rc; | |
} | |
+static void recover_underrun_work(struct work_struct *work) | |
+{ | |
+ struct mdss_mdp_ctl *ctl = | |
+ container_of(work, typeof(*ctl), recover_work); | |
+ | |
+ if (!ctl || !ctl->add_vsync_handler) { | |
+ pr_err("ctl or vsync handler is NULL\n"); | |
+ return; | |
+ } | |
+ | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ ctl->add_vsync_handler(ctl, &ctl->recover_underrun_handler); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
+} | |
+ | |
static void mdss_mdp_video_underrun_intr_done(void *arg) | |
{ | |
struct mdss_mdp_ctl *ctl = arg; | |
@@ -462,11 +496,73 @@ static void mdss_mdp_video_underrun_intr_done(void *arg) | |
return; | |
ctl->underrun_cnt++; | |
+ MDSS_XLOG(ctl->num, ctl->underrun_cnt); | |
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", "edp", "hdmi", "panic"); | |
+ trace_mdp_video_underrun_done(ctl->num, ctl->underrun_cnt); | |
pr_debug("display underrun detected for ctl=%d count=%d\n", ctl->num, | |
ctl->underrun_cnt); | |
+ | |
+ if (ctl->opmode & MDSS_MDP_CTL_OP_PACK_3D_ENABLE) | |
+ schedule_work(&ctl->recover_work); | |
} | |
-static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps) | |
+static int mdss_mdp_video_vfp_fps_update(struct mdss_mdp_ctl *ctl, int new_fps) | |
+{ | |
+ int curr_fps; | |
+ u32 add_v_lines = 0; | |
+ u32 current_vsync_period_f0, new_vsync_period_f0; | |
+ struct mdss_panel_data *pdata; | |
+ struct mdss_mdp_video_ctx *ctx; | |
+ u32 vsync_period, hsync_period; | |
+ | |
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; | |
+ if (!ctx) { | |
+ pr_err("invalid ctx\n"); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ pdata = ctl->panel_data; | |
+ if (pdata == NULL) { | |
+ pr_err("%s: Invalid panel data\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ vsync_period = mdss_panel_get_vtotal(&pdata->panel_info); | |
+ hsync_period = mdss_panel_get_htotal(&pdata->panel_info); | |
+ curr_fps = mdss_panel_get_framerate(&pdata->panel_info); | |
+ | |
+ if (curr_fps > new_fps) { | |
+ add_v_lines = mult_frac(vsync_period, | |
+ (curr_fps - new_fps), new_fps); | |
+ pdata->panel_info.lcdc.v_front_porch += add_v_lines; | |
+ } else { | |
+ add_v_lines = mult_frac(vsync_period, | |
+ (new_fps - curr_fps), new_fps); | |
+ pdata->panel_info.lcdc.v_front_porch -= add_v_lines; | |
+ } | |
+ | |
+ vsync_period = mdss_panel_get_vtotal(&pdata->panel_info); | |
+ current_vsync_period_f0 = mdp_video_read(ctx, | |
+ MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0); | |
+ new_vsync_period_f0 = (vsync_period * hsync_period); | |
+ | |
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0, | |
+ current_vsync_period_f0 | 0x800000); | |
+ if (new_vsync_period_f0 & 0x800000) { | |
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0, | |
+ new_vsync_period_f0); | |
+ } else { | |
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0, | |
+ new_vsync_period_f0 | 0x800000); | |
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0, | |
+ new_vsync_period_f0 & 0x7fffff); | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_mdp_ctl *sctl, int new_fps) | |
{ | |
struct mdss_mdp_video_ctx *ctx; | |
struct mdss_panel_data *pdata; | |
@@ -529,6 +625,50 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps) | |
ctl->force_screen_state = MDSS_SCREEN_DEFAULT; | |
mdss_mdp_display_commit(ctl, NULL); | |
mdss_mdp_display_wait4comp(ctl); | |
+ } else if (pdata->panel_info.dfps_update | |
+ == DFPS_IMMEDIATE_PORCH_UPDATE_MODE){ | |
+ if (!ctx->timegen_en) { | |
+ pr_err("TG is OFF. DFPS mode invalid\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ video_vsync_irq_enable(ctl, true); | |
+ INIT_COMPLETION(ctx->vsync_comp); | |
+ rc = wait_for_completion_timeout(&ctx->vsync_comp, | |
+ usecs_to_jiffies(VSYNC_TIMEOUT_US)); | |
+ WARN(rc <= 0, "timeout (%d) vsync interrupt on ctl=%d\n", | |
+ rc, ctl->num); | |
+ | |
+ video_vsync_irq_disable(ctl); | |
+ /* Do not configure fps on vsync timeout */ | |
+ if (rc <= 0) | |
+ return rc; | |
+ | |
+ if (mdss_mdp_video_line_count(ctl) >= | |
+ pdata->panel_info.yres/2) { | |
+ pr_err("Too few lines left line_cnt = %d y_res/2 = %d\n", | |
+ mdss_mdp_video_line_count(ctl), | |
+ pdata->panel_info.yres/2); | |
+ return -EPERM; | |
+ } | |
+ rc = mdss_mdp_video_vfp_fps_update(ctl, new_fps); | |
+ if (rc < 0) { | |
+ pr_err("%s: Error during DFPS\n", __func__); | |
+ return rc; | |
+ } | |
+ if (sctl) { | |
+ rc = mdss_mdp_video_vfp_fps_update(sctl, | |
+ new_fps); | |
+ if (rc < 0) { | |
+ pr_err("%s: DFPS error\n", __func__); | |
+ return rc; | |
+ } | |
+ } | |
+ rc = mdss_mdp_ctl_intf_event(ctl, | |
+ MDSS_EVENT_PANEL_UPDATE_FPS, | |
+ (void *)new_fps); | |
+ WARN(rc, "intf %d panel fps update error (%d)\n", | |
+ ctl->intf_num, rc); | |
} else { | |
pr_err("intf %d panel, unknown FPS mode\n", | |
ctl->intf_num); | |
@@ -545,12 +685,11 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps) | |
return rc; | |
} | |
- | |
- | |
- | |
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) | |
{ | |
struct mdss_mdp_video_ctx *ctx; | |
+ struct mdss_mdp_ctl *sctl; | |
+ struct mdss_panel_data *pdata = ctl->panel_data; | |
int rc; | |
pr_debug("kickoff ctl=%d\n", ctl->num); | |
@@ -569,6 +708,8 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) | |
WARN(1, "commit without wait! ctl=%d", ctl->num); | |
} | |
+ MDSS_XLOG(ctl->num, ctl->underrun_cnt); | |
+ | |
if (!ctx->timegen_en) { | |
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL); | |
if (rc) { | |
@@ -581,9 +722,28 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) | |
pr_debug("enabling timing gen for intf=%d\n", ctl->intf_num); | |
+ if (pdata->panel_info.cont_splash_enabled && | |
+ !ctl->mfd->splash_info.splash_logo_enabled) { | |
+ rc = wait_for_completion_timeout(&ctx->vsync_comp, | |
+ usecs_to_jiffies(VSYNC_TIMEOUT_US)); | |
+ } | |
+ | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ return rc; | |
+ } | |
+ | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num); | |
+ sctl = mdss_mdp_get_split_ctl(ctl); | |
+ if (sctl) | |
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, | |
+ sctl->intf_num); | |
+ | |
+ mdss_bus_bandwidth_ctrl(true); | |
+ | |
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1); | |
wmb(); | |
@@ -605,7 +765,6 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, | |
{ | |
struct mdss_panel_data *pdata = ctl->panel_data; | |
int i, ret = 0; | |
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(ctl->mfd); | |
struct mdss_mdp_video_ctx *ctx; | |
struct mdss_data_type *mdata = ctl->mdata; | |
@@ -640,13 +799,6 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, | |
error: | |
pdata->panel_info.cont_splash_enabled = 0; | |
- | |
- /* Give back the reserved memory to the system */ | |
- memblock_free(mdp5_data->splash_mem_addr, mdp5_data->splash_mem_size); | |
- free_bootmem_late(mdp5_data->splash_mem_addr, | |
- mdp5_data->splash_mem_size); | |
- | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
return ret; | |
} | |
@@ -683,6 +835,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) | |
return -EINVAL; | |
} | |
+ MDSS_XLOG(ctl->num, ctl->vsync_cnt); | |
pr_debug("start ctl=%u\n", ctl->num); | |
ctl->priv_data = ctx; | |
@@ -691,6 +844,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) | |
spin_lock_init(&ctx->vsync_lock); | |
mutex_init(&ctx->vsync_mtx); | |
atomic_set(&ctx->vsync_ref, 0); | |
+ INIT_WORK(&ctl->recover_work, recover_underrun_work); | |
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num, | |
mdss_mdp_video_vsync_intr_done, ctl); | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c | |
index 5c6a883..55b831a 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -17,6 +17,9 @@ | |
#include "mdss_mdp_rotator.h" | |
#include "mdss_panel.h" | |
+#define VBIF_WR_LIM_CONF 0xC0 | |
+#define MDSS_DEFAULT_OT_SETTING 0x10 | |
+ | |
enum mdss_mdp_writeback_type { | |
MDSS_MDP_WRITEBACK_TYPE_ROTATOR, | |
MDSS_MDP_WRITEBACK_TYPE_LINE, | |
@@ -34,11 +37,14 @@ struct mdss_mdp_writeback_ctx { | |
u32 intr_type; | |
u32 intf_num; | |
+ u32 xin_id; | |
+ u32 wr_lim; | |
+ | |
u32 opmode; | |
struct mdss_mdp_format_params *dst_fmt; | |
u16 width; | |
u16 height; | |
- struct mdss_rect dst_rect; | |
+ struct mdss_mdp_img_rect dst_rect; | |
u8 rot90; | |
u32 bwc_mode; | |
@@ -55,26 +61,31 @@ static struct mdss_mdp_writeback_ctx wb_ctx_list[MDSS_MDP_MAX_WRITEBACK] = { | |
.type = MDSS_MDP_WRITEBACK_TYPE_ROTATOR, | |
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP, | |
.intf_num = 0, | |
+ .xin_id = 3, | |
}, | |
{ | |
.type = MDSS_MDP_WRITEBACK_TYPE_ROTATOR, | |
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP, | |
.intf_num = 1, | |
+ .xin_id = 11, | |
}, | |
{ | |
.type = MDSS_MDP_WRITEBACK_TYPE_LINE, | |
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP, | |
.intf_num = 0, | |
+ .xin_id = 3, | |
}, | |
{ | |
.type = MDSS_MDP_WRITEBACK_TYPE_LINE, | |
.intr_type = MDSS_MDP_IRQ_WB_ROT_COMP, | |
.intf_num = 1, | |
+ .xin_id = 11, | |
}, | |
{ | |
.type = MDSS_MDP_WRITEBACK_TYPE_WFD, | |
.intr_type = MDSS_MDP_IRQ_WB_WFD, | |
.intf_num = 0, | |
+ .xin_id = 6, | |
}, | |
}; | |
@@ -431,6 +442,8 @@ static int mdss_mdp_wb_wait4comp(struct mdss_mdp_ctl *ctl, void *arg) | |
rc = 0; | |
} | |
+ mdss_iommu_ctrl(0); | |
+ mdss_bus_bandwidth_ctrl(false); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); /* clock off */ | |
ctx->comp_cnt--; | |
@@ -442,9 +455,12 @@ static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg) | |
{ | |
struct mdss_mdp_writeback_ctx *ctx; | |
struct mdss_mdp_writeback_arg *wb_args; | |
- u32 flush_bits; | |
+ u32 flush_bits, val, off; | |
int ret; | |
+ if (!ctl || !ctl->mdata) | |
+ return -ENODEV; | |
+ | |
ctx = (struct mdss_mdp_writeback_ctx *) ctl->priv_data; | |
if (!ctx) | |
return -ENODEV; | |
@@ -455,6 +471,18 @@ static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg) | |
return -EPERM; | |
} | |
+ if (ctl->mdata->rotator_ot_limit) { | |
+ if (ctx->type == MDSS_MDP_WRITEBACK_TYPE_ROTATOR) | |
+ ctx->wr_lim = ctl->mdata->rotator_ot_limit; | |
+ else | |
+ ctx->wr_lim = MDSS_DEFAULT_OT_SETTING; | |
+ off = (ctx->xin_id % 4) * 8; | |
+ val = readl_relaxed(ctl->mdata->vbif_base + VBIF_WR_LIM_CONF); | |
+ val &= ~(0xFF << off); | |
+ val |= (ctx->wr_lim) << off; | |
+ writel_relaxed(val, ctl->mdata->vbif_base + VBIF_WR_LIM_CONF); | |
+ } | |
+ | |
wb_args = (struct mdss_mdp_writeback_arg *) arg; | |
if (!wb_args) | |
return -ENOENT; | |
@@ -475,7 +503,13 @@ static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg) | |
INIT_COMPLETION(ctx->wb_comp); | |
mdss_mdp_irq_enable(ctx->intr_type, ctx->intf_num); | |
+ ret = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(ret)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ return ret; | |
+ } | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ mdss_bus_bandwidth_ctrl(true); | |
mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_START, 1); | |
wmb(); | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c | |
index 2f2c862..5b99105 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c | |
@@ -40,11 +40,16 @@ | |
#define CHECK_BOUNDS(offset, size, max_size) \ | |
(((size) > (max_size)) || ((offset) > ((max_size) - (size)))) | |
+#define IS_RIGHT_MIXER_OV(flags, dst_x, left_lm_w) \ | |
+ ((flags & MDSS_MDP_RIGHT_MIXER) || (dst_x >= left_lm_w)) | |
+ | |
#define PP_CLK_CFG_OFF 0 | |
#define PP_CLK_CFG_ON 1 | |
#define MEM_PROTECT_SD_CTRL 0xF | |
+#define OVERLAY_MAX 10 | |
+ | |
struct sd_ctrl_req { | |
unsigned int enable; | |
} __attribute__ ((__packed__)); | |
@@ -53,8 +58,14 @@ static atomic_t ov_active_panels = ATOMIC_INIT(0); | |
static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd); | |
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd); | |
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd); | |
-static int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd); | |
static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd); | |
+static void __vsync_retire_signal(struct msm_fb_data_type *mfd, int val); | |
+ | |
+static inline u32 left_lm_w_from_mfd(struct msm_fb_data_type *mfd) | |
+{ | |
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
+ return ctl->mixer_left->width; | |
+} | |
static int mdss_mdp_overlay_sd_ctrl(struct msm_fb_data_type *mfd, | |
unsigned int enable) | |
@@ -94,17 +105,71 @@ static int mdss_mdp_overlay_get(struct msm_fb_data_type *mfd, | |
return 0; | |
} | |
+/* | |
+ * This function is modified from mainline version. Source-split | |
+ * change is too large to port over onto certain code bases. | |
+ * Source-split patch added a new way to determine if layer | |
+ * is intended for right panel, by using x offset >= left LM | |
+ * This function corrects the x offset. | |
+ * Additionally, patches that fix other issues assumes that checks | |
+ * and corrections in this function are in place. | |
+ */ | |
+static int mdss_mdp_ov_xres_check(struct msm_fb_data_type *mfd, | |
+ struct mdp_overlay *req) | |
+{ | |
+ u32 xres = 0; | |
+ u32 left_lm_w = left_lm_w_from_mfd(mfd); | |
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
+ | |
+ if (IS_RIGHT_MIXER_OV(req->flags, req->dst_rect.x, left_lm_w)) { | |
+ if (req->dst_rect.x >= left_lm_w) { | |
+ /* | |
+ * this is a step towards removing a reliance on | |
+ * MDSS_MDP_RIGHT_MIXER flags. With the new src split | |
+ * code, some clients of non-src-split chipsets have | |
+ * stopped sending MDSS_MDP_RIGHT_MIXER flag and | |
+ * modified their xres relative to full panel | |
+ * dimensions. In such cases, we need to deduct left | |
+ * layer mixer width before we programm this HW. | |
+ */ | |
+ req->dst_rect.x -= left_lm_w; | |
+ req->flags |= MDSS_MDP_RIGHT_MIXER; | |
+ } | |
+ | |
+ if (ctl->mixer_right) { | |
+ xres += ctl->mixer_right->width; | |
+ } else { | |
+ pr_err("ov cannot be placed on right mixer\n"); | |
+ return -EPERM; | |
+ } | |
+ } else { | |
+ if (ctl->mixer_left) { | |
+ xres = ctl->mixer_left->width; | |
+ } else { | |
+ pr_err("ov cannot be placed on left mixer\n"); | |
+ return -EPERM; | |
+ } | |
+ } | |
+ | |
+ if (CHECK_BOUNDS(req->dst_rect.x, req->dst_rect.w, xres)) { | |
+ pr_err("dst_xres is invalid. dst_x:%d, dst_w:%d, xres:%d\n", | |
+ req->dst_rect.x, req->dst_rect.w, xres); | |
+ return -EOVERFLOW; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd, | |
struct mdp_overlay *req, | |
struct mdss_mdp_format_params *fmt) | |
{ | |
- u32 xres, yres; | |
+ u32 yres; | |
u32 min_src_size, min_dst_size; | |
int content_secure; | |
struct mdss_data_type *mdata = mfd_to_mdata(mfd); | |
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
- xres = mfd->fbi->var.xres; | |
yres = mfd->fbi->var.yres; | |
content_secure = (req->flags & MDP_SECURE_OVERLAY_SESSION); | |
@@ -153,14 +218,19 @@ int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd, | |
pr_err("Invalid decimation factors horz=%d vert=%d\n", | |
req->horz_deci, req->vert_deci); | |
return -EINVAL; | |
+ } else if (req->flags & MDP_BWC_EN) { | |
+ pr_err("Decimation can't be enabled with BWC\n"); | |
+ return -EINVAL; | |
+ } else if (fmt->tile) { | |
+ pr_err("Decimation can't be enabled with MacroTile format\n"); | |
+ return -EINVAL; | |
} | |
} | |
if (!(req->flags & MDSS_MDP_ROT_ONLY)) { | |
u32 src_w, src_h, dst_w, dst_h; | |
- if ((CHECK_BOUNDS(req->dst_rect.x, req->dst_rect.w, xres) || | |
- CHECK_BOUNDS(req->dst_rect.y, req->dst_rect.h, yres))) { | |
+ if (CHECK_BOUNDS(req->dst_rect.y, req->dst_rect.h, yres)) { | |
pr_err("invalid destination rect=%d,%d,%d,%d\n", | |
req->dst_rect.x, req->dst_rect.y, | |
req->dst_rect.w, req->dst_rect.h); | |
@@ -175,8 +245,8 @@ int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd, | |
dst_h = req->dst_rect.h; | |
} | |
- src_w = req->src_rect.w >> req->horz_deci; | |
- src_h = req->src_rect.h >> req->vert_deci; | |
+ src_w = DECIMATED_DIMENSION(req->src_rect.w, req->horz_deci); | |
+ src_h = DECIMATED_DIMENSION(req->src_rect.h, req->vert_deci); | |
if (src_w > MAX_MIXER_WIDTH) { | |
pr_err("invalid source width=%d HDec=%d\n", | |
@@ -221,7 +291,8 @@ int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd, | |
} | |
} | |
- if (req->flags & MDP_DEINTERLACE) { | |
+ if ((req->flags & MDP_DEINTERLACE) && | |
+ !req->scale.enable_pxl_ext) { | |
if (req->flags & MDP_SOURCE_ROTATED_90) { | |
if ((req->src_rect.w % 4) != 0) { | |
pr_err("interlaced rect not h/4\n"); | |
@@ -259,8 +330,7 @@ static int __mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe) | |
int rc; | |
for (;;) { | |
- rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL, | |
- pipe->flags & MDP_SECURE_OVERLAY_SESSION); | |
+ rc = mdss_mdp_perf_calc_pipe(pipe, &perf, NULL, true); | |
if (!rc && (perf.mdp_clk_rate <= mdata->max_mdp_clk_rate)) | |
break; | |
@@ -270,7 +340,9 @@ static int __mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe) | |
* requirement by applying vertical decimation and reduce | |
* mdp clock requirement | |
*/ | |
- if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION)) | |
+ if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION) | |
+ && !pipe->bwc_mode && !pipe->src_fmt->tile && | |
+ !pipe->scale.enable_pxl_ext) | |
pipe->vert_deci++; | |
else | |
return -EPERM; | |
@@ -279,15 +351,99 @@ static int __mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe) | |
return 0; | |
} | |
+static int __mdss_mdp_validate_pxl_extn(struct mdss_mdp_pipe *pipe) | |
+{ | |
+ int plane; | |
+ | |
+ for (plane = 0; plane < MAX_PLANES; plane++) { | |
+ u32 hor_req_pixels, hor_fetch_pixels; | |
+ u32 hor_ov_fetch, vert_ov_fetch; | |
+ u32 vert_req_pixels, vert_fetch_pixels; | |
+ u32 src_w = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci); | |
+ u32 src_h = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci); | |
+ | |
+ /* | |
+ * plane 1 and 2 are for chroma and are same. While configuring | |
+ * HW, programming only one of the chroma components is | |
+ * sufficient. | |
+ */ | |
+ if (plane == 2) | |
+ continue; | |
+ | |
+ /* | |
+ * For chroma plane, width is half for the following sub sampled | |
+ * formats. Except in case of decimation, where hardware avoids | |
+ * 1 line of decimation instead of downsampling. | |
+ */ | |
+ if (plane == 1 && !pipe->horz_deci && | |
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) || | |
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1))) { | |
+ src_w >>= 1; | |
+ } | |
+ | |
+ if (plane == 1 && !pipe->vert_deci && | |
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) || | |
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2))) | |
+ src_h >>= 1; | |
+ | |
+ hor_req_pixels = pipe->scale.roi_w[plane] + | |
+ pipe->scale.num_ext_pxls_left[plane] + | |
+ pipe->scale.num_ext_pxls_right[plane]; | |
+ | |
+ hor_fetch_pixels = src_w + | |
+ (pipe->scale.left_ftch[plane] >> pipe->horz_deci) + | |
+ pipe->scale.left_rpt[plane] + | |
+ (pipe->scale.right_ftch[plane] >> pipe->horz_deci) + | |
+ pipe->scale.right_rpt[plane]; | |
+ | |
+ hor_ov_fetch = src_w + | |
+ (pipe->scale.left_ftch[plane] >> pipe->horz_deci)+ | |
+ (pipe->scale.right_ftch[plane] >> pipe->horz_deci); | |
+ | |
+ vert_req_pixels = pipe->scale.num_ext_pxls_top[plane] + | |
+ pipe->scale.num_ext_pxls_btm[plane]; | |
+ | |
+ vert_fetch_pixels = | |
+ (pipe->scale.top_ftch[plane] >> pipe->vert_deci) + | |
+ pipe->scale.top_rpt[plane] + | |
+ (pipe->scale.btm_ftch[plane] >> pipe->vert_deci)+ | |
+ pipe->scale.btm_rpt[plane]; | |
+ | |
+ vert_ov_fetch = src_h + | |
+ (pipe->scale.top_ftch[plane] >> pipe->vert_deci)+ | |
+ (pipe->scale.btm_ftch[plane] >> pipe->vert_deci); | |
+ | |
+ if ((hor_req_pixels != hor_fetch_pixels) || | |
+ (hor_ov_fetch > pipe->img_width) || | |
+ (vert_req_pixels != vert_fetch_pixels) || | |
+ (vert_ov_fetch > pipe->img_height)) { | |
+ pr_err("err: plane=%d h_req:%d h_fetch:%d v_req:%d v_fetch:%d\n", | |
+ plane, | |
+ hor_req_pixels, hor_fetch_pixels, | |
+ vert_req_pixels, vert_fetch_pixels); | |
+ pr_err("roi_w[%d]=%d, src_img:[%d, %d]\n", | |
+ plane, pipe->scale.roi_w[plane], | |
+ pipe->img_width, pipe->img_height); | |
+ pipe->scale.enable_pxl_ext = 0; | |
+ return -EINVAL; | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
static int __mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe) | |
{ | |
u32 src; | |
int rc; | |
- src = pipe->src.w >> pipe->horz_deci; | |
+ src = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci); | |
+ | |
+ if (pipe->scale.enable_pxl_ext) { | |
+ rc = __mdss_mdp_validate_pxl_extn(pipe); | |
+ return rc; | |
+ } | |
- if (pipe->scale.enable_pxl_ext) | |
- return 0; | |
memset(&pipe->scale, 0, sizeof(struct mdp_scale_data)); | |
rc = mdss_mdp_calc_phase_step(src, pipe->dst.w, | |
&pipe->scale.phase_step_x[0]); | |
@@ -300,7 +456,7 @@ static int __mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe) | |
return rc; | |
} | |
- src = pipe->src.h >> pipe->vert_deci; | |
+ src = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci); | |
rc = mdss_mdp_calc_phase_step(src, pipe->dst.h, | |
&pipe->scale.phase_step_y[0]); | |
@@ -312,10 +468,6 @@ static int __mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe) | |
rc, src, pipe->dst.h); | |
return rc; | |
} | |
- pipe->scale.init_phase_x[0] = (pipe->scale.phase_step_x[0] - | |
- (1 << PHASE_STEP_SHIFT)) / 2; | |
- pipe->scale.init_phase_y[0] = (pipe->scale.phase_step_y[0] - | |
- (1 << PHASE_STEP_SHIFT)) / 2; | |
return rc; | |
} | |
@@ -342,7 +494,7 @@ static inline void __mdss_mdp_overlay_set_chroma_sample( | |
pipe->chroma_sample_v = 0; | |
} | |
-static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
+int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
struct mdp_overlay *req, | |
struct mdss_mdp_pipe **ppipe) | |
{ | |
@@ -354,13 +506,14 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
struct mdp_histogram_start_req hist; | |
int ret; | |
u32 bwc_enabled; | |
+ u32 left_lm_w = left_lm_w_from_mfd(mfd); | |
if (mdp5_data->ctl == NULL) | |
return -ENODEV; | |
if (req->flags & MDP_ROT_90) { | |
pr_err("unsupported inline rotation\n"); | |
- return -ENOTSUPP; | |
+ return -EOPNOTSUPP; | |
} | |
if ((req->dst_rect.w > MAX_DST_W) || (req->dst_rect.h > MAX_DST_H)) { | |
@@ -369,7 +522,7 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
return -EOVERFLOW; | |
} | |
- if (req->flags & MDSS_MDP_RIGHT_MIXER) | |
+ if (IS_RIGHT_MIXER_OV(req->flags, req->dst_rect.x, left_lm_w)) | |
mixer_mux = MDSS_MDP_MIXER_MUX_RIGHT; | |
else | |
mixer_mux = MDSS_MDP_MIXER_MUX_LEFT; | |
@@ -387,6 +540,10 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
return -EINVAL; | |
} | |
+ ret = mdss_mdp_ov_xres_check(mfd, req); | |
+ if (ret) | |
+ return ret; | |
+ | |
ret = mdss_mdp_overlay_req_check(mfd, req, fmt); | |
if (ret) | |
return ret; | |
@@ -439,7 +596,7 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
if (pipe == NULL) { | |
pr_err("error allocating pipe\n"); | |
- return -ENOMEM; | |
+ return -ENODEV; | |
} | |
ret = mdss_mdp_pipe_map(pipe); | |
@@ -448,9 +605,9 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
return ret; | |
} | |
- mutex_lock(&mfd->lock); | |
- list_add(&pipe->used_list, &mdp5_data->pipes_used); | |
- mutex_unlock(&mfd->lock); | |
+ mutex_lock(&mdp5_data->list_lock); | |
+ list_add(&pipe->list, &mdp5_data->pipes_used); | |
+ mutex_unlock(&mdp5_data->list_lock); | |
pipe->mixer = mixer; | |
pipe->mfd = mfd; | |
pipe->pid = current->tgid; | |
@@ -524,7 +681,8 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
pipe->overfetch_disable = OVERFETCH_DISABLE_BOTTOM; | |
if (!(pipe->flags & MDSS_MDP_DUAL_PIPE) || | |
- (pipe->flags & MDSS_MDP_RIGHT_MIXER)) | |
+ IS_RIGHT_MIXER_OV(req->flags, | |
+ req->dst_rect.x, left_lm_w)) | |
pipe->overfetch_disable |= OVERFETCH_DISABLE_RIGHT; | |
pr_debug("overfetch flags=%x\n", pipe->overfetch_disable); | |
} else { | |
@@ -586,6 +744,10 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
} | |
} | |
+ /* | |
+ * When scaling is enabled src crop and image | |
+ * width and height is modified by user | |
+ */ | |
if ((pipe->flags & MDP_DEINTERLACE) && !pipe->scale.enable_pxl_ext) { | |
if (pipe->flags & MDP_SOURCE_ROTATED_90) { | |
pipe->src.x = DIV_ROUND_UP(pipe->src.x, 2); | |
@@ -632,21 +794,21 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, | |
exit_fail: | |
mdss_mdp_pipe_unmap(pipe); | |
- mutex_lock(&mfd->lock); | |
+ mutex_lock(&mdp5_data->list_lock); | |
if (pipe->play_cnt == 0) { | |
pr_debug("failed for pipe %d\n", pipe->num); | |
- if (!list_empty(&pipe->used_list)) | |
- list_del_init(&pipe->used_list); | |
+ if (!list_empty(&pipe->list)) | |
+ list_del_init(&pipe->list); | |
mdss_mdp_pipe_destroy(pipe); | |
} | |
/* invalidate any overlays in this framebuffer after failure */ | |
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) { | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { | |
pr_debug("freeing allocations for pipe %d\n", pipe->num); | |
mdss_mdp_smp_unreserve(pipe); | |
pipe->params_changed = 0; | |
} | |
- mutex_unlock(&mfd->lock); | |
+ mutex_unlock(&mdp5_data->list_lock); | |
return ret; | |
} | |
@@ -691,11 +853,17 @@ int mdss_mdp_overlay_get_buf(struct msm_fb_data_type *mfd, | |
int num_planes, | |
u32 flags) | |
{ | |
- int i, rc = 0; | |
+ int i, rc; | |
if ((num_planes <= 0) || (num_planes > MAX_PLANES)) | |
return -EINVAL; | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) { | |
+ pr_err("Iommu attach failed"); | |
+ goto end; | |
+ } | |
+ | |
memset(data, 0, sizeof(*data)); | |
for (i = 0; i < num_planes; i++) { | |
data->p[i].flags = flags; | |
@@ -710,19 +878,27 @@ int mdss_mdp_overlay_get_buf(struct msm_fb_data_type *mfd, | |
} | |
} | |
+ mdss_iommu_ctrl(0); | |
data->num_planes = i; | |
- | |
+end: | |
return rc; | |
} | |
int mdss_mdp_overlay_free_buf(struct mdss_mdp_data *data) | |
{ | |
- int i; | |
+ int i, rc; | |
+ | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) { | |
+ pr_err("Iommu attach failed"); | |
+ return rc; | |
+ } | |
+ | |
for (i = 0; i < data->num_planes && data->p[i].len; i++) | |
mdss_mdp_put_img(&data->p[i]); | |
+ mdss_iommu_ctrl(0); | |
data->num_planes = 0; | |
- | |
return 0; | |
} | |
@@ -773,10 +949,9 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd) | |
bool recovery_mode = false; | |
LIST_HEAD(destroy_pipes); | |
- mutex_lock(&mfd->lock); | |
- list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_cleanup, | |
- cleanup_list) { | |
- list_move(&pipe->cleanup_list, &destroy_pipes); | |
+ mutex_lock(&mdp5_data->list_lock); | |
+ list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_cleanup, list) { | |
+ list_move(&pipe->list, &destroy_pipes); | |
/* make sure pipe fetch has been halted before freeing buffer */ | |
if (mdss_mdp_pipe_fetch_halt(pipe)) { | |
@@ -804,7 +979,7 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd) | |
__mdss_mdp_overlay_free_list_purge(mfd); | |
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) { | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { | |
if (pipe->back_buf.num_planes) { | |
/* make back buffer active */ | |
__mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf); | |
@@ -812,15 +987,22 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd) | |
} | |
} | |
- list_for_each_entry_safe(pipe, tmp, &destroy_pipes, cleanup_list) { | |
- __mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf); | |
+ list_for_each_entry_safe(pipe, tmp, &destroy_pipes, list) { | |
+ /* | |
+ * in case of secure UI, the buffer needs to be released as | |
+ * soon as session is closed. | |
+ */ | |
+ if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) | |
+ mdss_mdp_overlay_free_buf(&pipe->front_buf); | |
+ else | |
+ __mdss_mdp_overlay_free_list_add(mfd, &pipe->front_buf); | |
mdss_mdp_overlay_free_buf(&pipe->back_buf); | |
mdss_mdp_pipe_destroy(pipe); | |
} | |
- mutex_unlock(&mfd->lock); | |
+ mutex_unlock(&mdp5_data->list_lock); | |
} | |
-static void __mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd, | |
+void mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd, | |
u32 type) | |
{ | |
u32 i, npipes; | |
@@ -850,8 +1032,7 @@ static void __mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd, | |
pipe = &pipes[i]; | |
if (pipe->is_handed_off) { | |
pr_debug("Unmapping handed off pipe %d\n", pipe->num); | |
- list_add(&pipe->cleanup_list, | |
- &mdp5_data->pipes_cleanup); | |
+ list_add(&pipe->list, &mdp5_data->pipes_cleanup); | |
mdss_mdp_mixer_pipe_unstage(pipe); | |
pipe->is_handed_off = false; | |
} | |
@@ -867,17 +1048,27 @@ static void __mdss_mdp_handoff_cleanup_pipes(struct msm_fb_data_type *mfd, | |
* from the the splash screen to the android boot animation when the | |
* continuous splash screen feature is enabled. | |
*/ | |
-static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd) | |
+int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd) | |
{ | |
int rc; | |
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
struct mdss_mdp_ctl *ctl = mdp5_data->ctl; | |
if (ctl->power_on) { | |
+ if (mdp5_data->mdata->ulps) { | |
+ rc = mdss_mdp_footswitch_ctrl_ulps(1, &mfd->pdev->dev); | |
+ if (rc) { | |
+ pr_err("footswtich control power on failed rc=%d\n", | |
+ rc); | |
+ goto end; | |
+ } | |
+ | |
+ mdss_mdp_ctl_restore(ctl); | |
+ } | |
+ | |
if (!mdp5_data->mdata->batfet) | |
mdss_mdp_batfet_ctrl(mdp5_data->mdata, true); | |
- if (!mfd->panel_info->cont_splash_enabled) | |
- mdss_iommu_attach(mdp5_data->mdata); | |
+ mdss_mdp_release_splash_pipe(mfd); | |
return 0; | |
} | |
@@ -886,7 +1077,7 @@ static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd) | |
rc = pm_runtime_get_sync(&mfd->pdev->dev); | |
if (IS_ERR_VALUE(rc)) { | |
pr_err("unable to resume with pm_runtime_get_sync rc=%d\n", rc); | |
- return rc; | |
+ goto end; | |
} | |
/* | |
@@ -898,9 +1089,15 @@ static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd) | |
* we would have called in to TZ to restore security configs from LK. | |
*/ | |
if (!is_mdss_iommu_attached()) { | |
- if (!mfd->panel_info->cont_splash_enabled) | |
- mdss_iommu_attach(mdss_res); | |
- mdss_hw_init(mdss_res); | |
+ if (!mfd->panel_info->cont_splash_enabled) { | |
+ rc = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(rc)) { | |
+ pr_err("iommu attach failed rc=%d\n", rc); | |
+ goto pm_error; | |
+ } | |
+ mdss_hw_init(mdss_res); | |
+ mdss_iommu_ctrl(0); | |
+ } | |
} | |
rc = mdss_mdp_ctl_start(ctl, false); | |
@@ -911,65 +1108,19 @@ static int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd) | |
&mfd->mdp_sync_pt_data.notifier); | |
} else { | |
pr_err("mdp ctl start failed.\n"); | |
- goto error; | |
+ goto ctl_error; | |
} | |
- if (mfd->panel_info->cont_splash_enabled) { | |
- if (mdp5_data->handoff) { | |
- /* | |
- * Set up border-fill on the handed off pipes. | |
- * This is needed to ensure that there are no memory | |
- * accesses prior to attaching iommu during continuous | |
- * splash screen case. However, for command mode | |
- * displays, this is not necessary since the panels can | |
- * refresh from their internal memory if no data is sent | |
- * out on the dsi lanes. | |
- */ | |
- if (ctl && ctl->is_video_mode) { | |
- rc = mdss_mdp_display_commit(ctl, NULL); | |
- if (!IS_ERR_VALUE(rc)) { | |
- mdss_mdp_display_wait4comp(ctl); | |
- } else { | |
- /* | |
- * Since border-fill setup failed, we | |
- * need to ensure that we turn off the | |
- * MDP timing generator before attaching | |
- * iommu | |
- */ | |
- pr_err("failed to set BF at handoff\n"); | |
- mdp5_data->handoff = false; | |
- rc = 0; | |
- } | |
- } | |
- | |
- /* Add all the handed off pipes to the cleanup list */ | |
- __mdss_mdp_handoff_cleanup_pipes(mfd, | |
- MDSS_MDP_PIPE_TYPE_RGB); | |
- __mdss_mdp_handoff_cleanup_pipes(mfd, | |
- MDSS_MDP_PIPE_TYPE_VIG); | |
- __mdss_mdp_handoff_cleanup_pipes(mfd, | |
- MDSS_MDP_PIPE_TYPE_DMA); | |
- } | |
- rc = mdss_mdp_ctl_splash_finish(ctl, mdp5_data->handoff); | |
- /* | |
- * Remove the vote for footswitch even if above function | |
- * returned error | |
- */ | |
- mdss_mdp_footswitch_ctrl_splash(0); | |
- if (rc) | |
- goto error; | |
- | |
- if (!is_mdss_iommu_attached()) | |
- mdss_iommu_attach(mdss_res); | |
- } | |
- | |
-error: | |
- if (rc) { | |
- mdss_mdp_ctl_destroy(ctl); | |
- mdp5_data->ctl = NULL; | |
- pm_runtime_put(&mfd->pdev->dev); | |
- } | |
+ rc = mdss_mdp_splash_cleanup(mfd, true); | |
+ if (!rc) | |
+ goto end; | |
+ctl_error: | |
+ mdss_mdp_ctl_destroy(ctl); | |
+ mdp5_data->ctl = NULL; | |
+pm_error: | |
+ pm_runtime_put(&mfd->pdev->dev); | |
+end: | |
return rc; | |
} | |
@@ -994,7 +1145,16 @@ static int __overlay_queue_pipes(struct msm_fb_data_type *mfd) | |
struct mdss_mdp_ctl *tmp; | |
int ret = 0; | |
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) { | |
+ /* | |
+ * Setup pipe in solid fill before unstaging, | |
+ * to ensure no fetches are happening after dettach or reattach. | |
+ */ | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, list) { | |
+ mdss_mdp_pipe_queue_data(pipe, NULL); | |
+ mdss_mdp_mixer_pipe_unstage(pipe); | |
+ } | |
+ | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { | |
struct mdss_mdp_data *buf; | |
/* | |
* When secure display is enabled, if there is a non secure | |
@@ -1032,11 +1192,13 @@ static int __overlay_queue_pipes(struct msm_fb_data_type *mfd) | |
pipe->mixer = mdss_mdp_mixer_get(tmp, | |
MDSS_MDP_MIXER_MUX_DEFAULT); | |
} | |
+ | |
+ /* ensure pipes are always reconfigured after power off/on */ | |
+ if (ctl->play_cnt == 0) | |
+ pipe->params_changed++; | |
+ | |
if (pipe->back_buf.num_planes) { | |
buf = &pipe->back_buf; | |
- } else if (ctl->play_cnt == 0 && pipe->front_buf.num_planes) { | |
- pipe->params_changed++; | |
- buf = &pipe->front_buf; | |
} else if (!pipe->params_changed) { | |
continue; | |
} else if (pipe->front_buf.num_planes) { | |
@@ -1065,7 +1227,9 @@ static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd) | |
mdss_mdp_display_commit(ctl, NULL); | |
mdss_mdp_display_wait4comp(ctl); | |
+ ATRACE_BEGIN("sspp_programming"); | |
__overlay_queue_pipes(mfd); | |
+ ATRACE_END("sspp_programming"); | |
mdss_mdp_display_commit(ctl, NULL); | |
mdss_mdp_display_wait4comp(ctl); | |
@@ -1079,17 +1243,22 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, | |
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
int ret = 0; | |
int sd_in_pipe = 0; | |
+ bool need_cleanup = false; | |
- if (ctl->shared_lock) | |
+ ATRACE_BEGIN(__func__); | |
+ if (ctl->shared_lock) { | |
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN); | |
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY); | |
mutex_lock(ctl->shared_lock); | |
+ } | |
mutex_lock(&mdp5_data->ov_lock); | |
- mutex_lock(&mfd->lock); | |
+ mutex_lock(&mdp5_data->list_lock); | |
/* | |
* check if there is a secure display session | |
*/ | |
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) { | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { | |
if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) { | |
sd_in_pipe = 1; | |
pr_debug("Secure pipe: %u : %08X\n", | |
@@ -1105,7 +1274,9 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, | |
mdp5_data->sd_enabled = 0; | |
} | |
- mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN); | |
+ if (!ctl->shared_lock) | |
+ mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN); | |
+ | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
if (data) | |
@@ -1115,78 +1286,112 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd, | |
* Setup pipe in solid fill before unstaging, | |
* to ensure no fetches are happening after dettach or reattach. | |
*/ | |
- list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, cleanup_list) { | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, list) { | |
mdss_mdp_pipe_queue_data(pipe, NULL); | |
mdss_mdp_mixer_pipe_unstage(pipe); | |
+ need_cleanup = true; | |
} | |
+ ATRACE_BEGIN("sspp_programming"); | |
ret = __overlay_queue_pipes(mfd); | |
+ ATRACE_END("sspp_programming"); | |
- if (mfd->panel.type == WRITEBACK_PANEL) | |
+ mutex_unlock(&mdp5_data->list_lock); | |
+ if (mfd->panel.type == WRITEBACK_PANEL) { | |
+ ATRACE_BEGIN("wb_kickoff"); | |
ret = mdss_mdp_wb_kickoff(mfd); | |
- else | |
+ ATRACE_END("wb_kickoff"); | |
+ } else { | |
+ ATRACE_BEGIN("display_commit"); | |
ret = mdss_mdp_display_commit(mdp5_data->ctl, NULL); | |
+ ATRACE_END("display_commit"); | |
+ } | |
- mutex_unlock(&mfd->lock); | |
+ if (!need_cleanup) { | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
+ wake_up_all(&mfd->kickoff_wait_q); | |
+ } | |
if (IS_ERR_VALUE(ret)) | |
goto commit_fail; | |
+ mutex_unlock(&mdp5_data->ov_lock); | |
mdss_mdp_overlay_update_pm(mdp5_data); | |
+ ATRACE_BEGIN("display_wait4comp"); | |
ret = mdss_mdp_display_wait4comp(mdp5_data->ctl); | |
+ ATRACE_END("display_wait4comp"); | |
+ mutex_lock(&mdp5_data->ov_lock); | |
if (ret == 0) { | |
- mutex_lock(&mfd->lock); | |
if (!mdp5_data->sd_enabled && (sd_in_pipe == 1)) { | |
ret = mdss_mdp_overlay_sd_ctrl(mfd, 1); | |
if (ret == 0) | |
mdp5_data->sd_enabled = 1; | |
} | |
- mutex_unlock(&mfd->lock); | |
} | |
mdss_fb_update_notify_update(mfd); | |
commit_fail: | |
+ ATRACE_BEGIN("overlay_cleanup"); | |
mdss_mdp_overlay_cleanup(mfd); | |
+ ATRACE_END("overlay_cleanup"); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_FLUSHED); | |
- | |
+ if (need_cleanup) { | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
+ wake_up_all(&mfd->kickoff_wait_q); | |
+ } | |
mutex_unlock(&mdp5_data->ov_lock); | |
if (ctl->shared_lock) | |
mutex_unlock(ctl->shared_lock); | |
- | |
+ ATRACE_END(__func__); | |
return ret; | |
} | |
-static int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx) | |
+int mdss_mdp_overlay_release(struct msm_fb_data_type *mfd, int ndx) | |
{ | |
- struct mdss_mdp_pipe *pipe; | |
+ struct mdss_mdp_pipe *pipe, *tmp; | |
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
- u32 pipe_ndx, unset_ndx = 0; | |
- int i; | |
- | |
- for (i = 0; unset_ndx != ndx && i < MDSS_MDP_MAX_SSPP; i++) { | |
- pipe_ndx = BIT(i); | |
- if (pipe_ndx & ndx) { | |
- unset_ndx |= pipe_ndx; | |
- pipe = mdss_mdp_pipe_get(mdp5_data->mdata, pipe_ndx); | |
- if (IS_ERR_OR_NULL(pipe)) { | |
- pr_warn("unknown pipe ndx=%x\n", pipe_ndx); | |
+ u32 unset_ndx = 0; | |
+ int destroy_pipe; | |
+ | |
+ mutex_lock(&mdp5_data->list_lock); | |
+ list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) { | |
+ if (pipe->ndx & ndx) { | |
+ if (mdss_mdp_pipe_map(pipe)) { | |
+ pr_err("Unable to map used pipe%d ndx=%x\n", | |
+ pipe->num, pipe->ndx); | |
continue; | |
} | |
- mutex_lock(&mfd->lock); | |
+ | |
+ unset_ndx |= pipe->ndx; | |
+ | |
pipe->pid = 0; | |
- if (!list_empty(&pipe->used_list)) { | |
- list_del_init(&pipe->used_list); | |
- list_add(&pipe->cleanup_list, | |
- &mdp5_data->pipes_cleanup); | |
- } | |
- mutex_unlock(&mfd->lock); | |
- mdss_mdp_mixer_pipe_unstage(pipe); | |
+ destroy_pipe = pipe->play_cnt == 0; | |
+ | |
+ if (destroy_pipe) | |
+ list_del_init(&pipe->list); | |
+ else | |
+ list_move(&pipe->list, | |
+ &mdp5_data->pipes_cleanup); | |
mdss_mdp_pipe_unmap(pipe); | |
+ if (destroy_pipe) | |
+ mdss_mdp_pipe_destroy(pipe); | |
+ | |
+ if (unset_ndx == ndx) | |
+ break; | |
} | |
} | |
+ | |
+ mutex_unlock(&mdp5_data->list_lock); | |
+ | |
+ if (unset_ndx != ndx) { | |
+ pr_warn("Unable to unset pipe(s) ndx=0x%x unset=0x%x\n", | |
+ ndx, unset_ndx); | |
+ return -ENOENT; | |
+ } | |
+ | |
return 0; | |
} | |
@@ -1254,16 +1459,16 @@ static int __mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd, | |
pr_debug("releasing all resources for fb%d pid=%d\n", mfd->index, pid); | |
mutex_lock(&mdp5_data->ov_lock); | |
- mutex_lock(&mfd->lock); | |
- list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) { | |
+ mutex_lock(&mdp5_data->list_lock); | |
+ list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { | |
if (release_all || (pipe->pid == pid)) { | |
unset_ndx |= pipe->ndx; | |
cnt++; | |
} | |
} | |
- if (cnt == 0 && !list_empty(&mdp5_data->pipes_cleanup)) { | |
- pr_debug("overlay release on fb%d called without commit!", | |
+ if (!mfd->ref_cnt && !list_empty(&mdp5_data->pipes_cleanup)) { | |
+ pr_debug("fb%d:: free pipes present in cleanup list", | |
mfd->index); | |
cnt++; | |
} | |
@@ -1271,7 +1476,7 @@ static int __mdss_mdp_overlay_release_all(struct msm_fb_data_type *mfd, | |
pr_debug("release_all=%d mfd->ref_cnt=%d unset_ndx=0x%x cnt=%d\n", | |
release_all, mfd->ref_cnt, unset_ndx, cnt); | |
- mutex_unlock(&mfd->lock); | |
+ mutex_unlock(&mdp5_data->list_lock); | |
if (unset_ndx) { | |
pr_debug("%d pipes need cleanup (%x)\n", cnt, unset_ndx); | |
@@ -1331,9 +1536,6 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd, | |
flags = (pipe->flags & MDP_SECURE_OVERLAY_SESSION); | |
flags |= (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION); | |
- if (!mfd->panel_info->cont_splash_enabled) | |
- mdss_iommu_attach(mdata); | |
- | |
src_data = &pipe->back_buf; | |
if (src_data->num_planes) { | |
pr_warn("dropped buffer pnum=%d play=%d addr=0x%x\n", | |
@@ -1345,6 +1547,7 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd, | |
if (IS_ERR_VALUE(ret)) { | |
pr_err("src_data pmem error\n"); | |
} | |
+ | |
mdss_mdp_pipe_unmap(pipe); | |
return ret; | |
@@ -1378,8 +1581,13 @@ static void mdss_mdp_overlay_force_dma_cleanup(struct mdss_data_type *mdata) | |
for (i = 0; i < mdata->ndma_pipes; i++) { | |
pipe = mdata->dma_pipes + i; | |
- if (atomic_read(&pipe->ref_cnt) && pipe->mfd) | |
- mdss_mdp_overlay_force_cleanup(pipe->mfd); | |
+ | |
+ if (!mdss_mdp_pipe_map(pipe)) { | |
+ struct msm_fb_data_type *mfd = pipe->mfd; | |
+ mdss_mdp_pipe_unmap(pipe); | |
+ if (mfd) | |
+ mdss_mdp_overlay_force_cleanup(mfd); | |
+ } | |
} | |
} | |
@@ -1455,14 +1663,16 @@ static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd, | |
{ | |
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
struct mdss_mdp_pipe *pipe; | |
+ int ret; | |
pipe = mdss_mdp_mixer_stage_pipe(mdp5_data->ctl, mixer_mux, | |
MDSS_MDP_STAGE_BASE); | |
+ | |
if (pipe == NULL) { | |
struct mdp_overlay req; | |
struct fb_info *fbi = mfd->fbi; | |
struct mdss_mdp_mixer *mixer; | |
- int ret, bpp; | |
+ int bpp; | |
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, | |
MDSS_MDP_MIXER_MUX_LEFT); | |
@@ -1483,19 +1693,17 @@ static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd, | |
pr_warn("right fb pipe not needed\n"); | |
return -EINVAL; | |
} | |
- | |
- req.flags |= MDSS_MDP_RIGHT_MIXER; | |
+ req.flags = req.flags | MDSS_MDP_RIGHT_MIXER; | |
req.src_rect.x = mixer->width; | |
req.src_rect.w = fbi->var.xres - mixer->width; | |
} else { | |
req.src_rect.x = 0; | |
- req.src_rect.w = MIN(fbi->var.xres, mixer->width); | |
+ req.src_rect.w = MIN(fbi->var.xres, | |
+ mixer->width); | |
} | |
req.src_rect.y = 0; | |
req.src_rect.h = req.src.height; | |
- req.dst_rect.x = 0; | |
- req.dst_rect.y = 0; | |
req.dst_rect.w = req.src_rect.w; | |
req.dst_rect.h = req.src_rect.h; | |
req.z_order = MDSS_MDP_STAGE_BASE; | |
@@ -1505,9 +1713,8 @@ static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd, | |
ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe); | |
if (ret) | |
return ret; | |
- | |
- pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num); | |
} | |
+ pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num); | |
*ppipe = pipe; | |
return 0; | |
@@ -1532,7 +1739,7 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
return; | |
if (!fbi->fix.smem_start || fbi->fix.smem_len == 0 || | |
- mdp5_data->borderfill_enable) { | |
+ mdp5_data->borderfill_enable) { | |
mfd->mdp.kickoff_fnc(mfd, NULL); | |
return; | |
} | |
@@ -1540,11 +1747,15 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
if (mutex_lock_interruptible(&mdp5_data->ov_lock)) | |
return; | |
- if (!mfd->panel_power_on) { | |
+ if ((!mfd->panel_power_on) && !((mfd->dcm_state == DCM_ENTER) && | |
+ (mfd->panel.type == MIPI_CMD_PANEL))) { | |
mutex_unlock(&mdp5_data->ov_lock); | |
return; | |
} | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ | |
+ | |
bpp = fbi->var.bits_per_pixel / 8; | |
offset = fbi->var.xoffset * bpp + | |
fbi->var.yoffset * fbi->fix.line_length; | |
@@ -1561,8 +1772,14 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
goto pan_display_error; | |
} | |
+ ret = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(ret)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ goto pan_display_error; | |
+ } | |
- ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_LEFT); | |
+ ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, | |
+ MDSS_MDP_MIXER_MUX_LEFT); | |
if (ret) { | |
pr_err("unable to allocate base pipe\n"); | |
goto pan_display_error; | |
@@ -1577,6 +1794,7 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
if (is_mdss_iommu_attached()) { | |
if (!mfd->iova) { | |
pr_err("mfd iova is zero\n"); | |
+ mdss_mdp_pipe_unmap(pipe); | |
goto pan_display_error; | |
} | |
buf->p[0].addr = mfd->iova; | |
@@ -1591,7 +1809,7 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
if (fbi->var.xres > MAX_MIXER_WIDTH || mfd->split_display) { | |
ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, | |
- MDSS_MDP_MIXER_MUX_RIGHT); | |
+ MDSS_MDP_MIXER_MUX_RIGHT); | |
if (ret) { | |
pr_err("unable to allocate right base pipe\n"); | |
goto pan_display_error; | |
@@ -1600,6 +1818,7 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
pr_err("unable to map right base pipe\n"); | |
goto pan_display_error; | |
} | |
+ | |
pipe->back_buf = *buf; | |
mdss_mdp_pipe_unmap(pipe); | |
} | |
@@ -1609,12 +1828,45 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) | |
(fbi->var.activate & FB_ACTIVATE_FORCE)) | |
mfd->mdp.kickoff_fnc(mfd, NULL); | |
+ mdss_iommu_ctrl(0); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
return; | |
pan_display_error: | |
+ mdss_iommu_ctrl(0); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
mutex_unlock(&mdp5_data->ov_lock); | |
} | |
+static void remove_underrun_vsync_handler(struct work_struct *work) | |
+{ | |
+ int rc; | |
+ struct mdss_mdp_ctl *ctl = | |
+ container_of(work, typeof(*ctl), remove_underrun_handler); | |
+ | |
+ if (!ctl || !ctl->remove_vsync_handler) { | |
+ pr_err("ctl or vsync handler is NULL\n"); | |
+ return; | |
+ } | |
+ | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ rc = ctl->remove_vsync_handler(ctl, | |
+ &ctl->recover_underrun_handler); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
+} | |
+ | |
+static void mdss_mdp_recover_underrun_handler(struct mdss_mdp_ctl *ctl, | |
+ ktime_t t) | |
+{ | |
+ if (!ctl) { | |
+ pr_err("ctl is NULL\n"); | |
+ return; | |
+ } | |
+ | |
+ mdss_mdp_ctl_reset(ctl); | |
+ schedule_work(&ctl->remove_underrun_handler); | |
+} | |
+ | |
/* function is called in irq context should have minimum processing */ | |
static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl, | |
ktime_t t) | |
@@ -1691,10 +1943,12 @@ static ssize_t dynamic_fps_sysfs_rda_dfps(struct device *dev, | |
return -ENODEV; | |
} | |
+ mutex_lock(&mdp5_data->dfps_lock); | |
ret = snprintf(buf, PAGE_SIZE, "%d\n", | |
pdata->panel_info.mipi.frame_rate); | |
pr_debug("%s: '%d'\n", __func__, | |
pdata->panel_info.mipi.frame_rate); | |
+ mutex_unlock(&mdp5_data->dfps_lock); | |
return ret; | |
} /* dynamic_fps_sysfs_rda_dfps */ | |
@@ -1729,6 +1983,7 @@ static ssize_t dynamic_fps_sysfs_wta_dfps(struct device *dev, | |
return count; | |
} | |
+ mutex_lock(&mdp5_data->dfps_lock); | |
if (dfps < 30) { | |
pr_err("Unsupported FPS. Configuring to min_fps = 30\n"); | |
dfps = 30; | |
@@ -1745,9 +2000,11 @@ static ssize_t dynamic_fps_sysfs_wta_dfps(struct device *dev, | |
} else { | |
pr_err("Failed to configure '%d' FPS. rc = %d\n", | |
dfps, rc); | |
+ mutex_unlock(&mdp5_data->dfps_lock); | |
return rc; | |
} | |
pdata->panel_info.new_fps = dfps; | |
+ mutex_unlock(&mdp5_data->dfps_lock); | |
return count; | |
} /* dynamic_fps_sysfs_wta_dfps */ | |
@@ -1780,7 +2037,7 @@ static ssize_t mdss_mdp_vsync_show_event(struct device *dev, | |
vsync_ticks = ktime_to_ns(mdp5_data->vsync_time); | |
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks); | |
- ret = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks); | |
+ ret = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks); | |
return ret; | |
} | |
@@ -1896,6 +2153,8 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, | |
mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_DEFAULT); | |
off = MDSS_MDP_REG_LM_OFFSET(mixer->num); | |
+ if (!mixer) | |
+ return -ENODEV; | |
if ((img->width > MDSS_MDP_CURSOR_WIDTH) || | |
(img->height > MDSS_MDP_CURSOR_HEIGHT) || | |
@@ -2021,6 +2280,11 @@ static int mdss_mdp_pp_ioctl(struct msm_fb_data_type *mfd, | |
u32 copyback = 0; | |
u32 copy_from_kernel = 0; | |
+ if (mfd->panel_info->partial_update_enabled) { | |
+ pr_err("Partical update feature is enabled."); | |
+ return -EPERM; | |
+ } | |
+ | |
ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp)); | |
if (ret) | |
return ret; | |
@@ -2134,6 +2398,11 @@ static int mdss_mdp_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd, | |
u32 pp_bus_handle; | |
static int req = -1; | |
+ if (mfd->panel_info->partial_update_enabled) { | |
+ pr_err("Partical update feature is enabled."); | |
+ return -EPERM; | |
+ } | |
+ | |
switch (cmd) { | |
case MSMFB_HISTOGRAM_START: | |
if (!mfd->panel_power_on) | |
@@ -2237,6 +2506,10 @@ static int mdss_fb_get_hw_caps(struct msm_fb_data_type *mfd, | |
caps->features |= MDP_BWC_EN; | |
if (mdata->has_decimation) | |
caps->features |= MDP_DECIMATION_EN; | |
+ | |
+ caps->max_smp_cnt = mdss_res->smp_mb_cnt; | |
+ caps->smp_per_pipe = mdata->smp_mb_per_pipe; | |
+ | |
return 0; | |
} | |
@@ -2279,12 +2552,15 @@ static int __handle_overlay_prepare(struct msm_fb_data_type *mfd, | |
struct mdp_overlay_list *ovlist, | |
struct mdp_overlay *overlays) | |
{ | |
+ struct mdss_mdp_pipe *right_plist[MDSS_MDP_MAX_STAGE] = { 0 }; | |
+ struct mdss_mdp_pipe *left_plist[MDSS_MDP_MAX_STAGE] = { 0 }; | |
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
struct mdss_mdp_pipe *pipe; | |
struct mdp_overlay *req; | |
- int ret = 0; | |
+ int ret = 0, left_cnt = 0, right_cnt = 0; | |
int i; | |
u32 new_reqs = 0; | |
+ u32 left_lm_w = left_lm_w_from_mfd(mfd); | |
ret = mutex_lock_interruptible(&mdp5_data->ov_lock); | |
if (ret) | |
@@ -2311,8 +2587,29 @@ static int __handle_overlay_prepare(struct msm_fb_data_type *mfd, | |
/* keep track of the new overlays to unset in case of errors */ | |
if (pipe->play_cnt == 0) | |
new_reqs |= pipe->ndx; | |
+ | |
+ if (IS_RIGHT_MIXER_OV(req->flags, req->dst_rect.x, left_lm_w)) { | |
+ if (right_cnt >= MDSS_MDP_MAX_STAGE) { | |
+ pr_err("too many pipes on right mixer\n"); | |
+ ret = -EINVAL; | |
+ goto validate_exit; | |
+ } | |
+ right_plist[right_cnt] = pipe; | |
+ right_cnt++; | |
+ } else { | |
+ if (left_cnt >= MDSS_MDP_MAX_STAGE) { | |
+ pr_err("too many pipes on left mixer\n"); | |
+ ret = -EINVAL; | |
+ goto validate_exit; | |
+ } | |
+ left_plist[left_cnt] = pipe; | |
+ left_cnt++; | |
+ } | |
} | |
+ ret = mdss_mdp_perf_bw_check(mdp5_data->ctl, left_plist, left_cnt, | |
+ right_plist, right_cnt); | |
+ | |
validate_exit: | |
if (IS_ERR_VALUE(ret)) | |
mdss_mdp_overlay_release(mfd, new_reqs); | |
@@ -2327,20 +2624,32 @@ static int __handle_ioctl_overlay_prepare(struct msm_fb_data_type *mfd, | |
void __user *argp) | |
{ | |
struct mdp_overlay_list ovlist; | |
+ struct mdp_overlay *req_list[OVERLAY_MAX]; | |
struct mdp_overlay *overlays; | |
int i, ret; | |
if (copy_from_user(&ovlist, argp, sizeof(ovlist))) | |
return -EFAULT; | |
+ if (ovlist.num_overlays >= OVERLAY_MAX) { | |
+ pr_err("Number of overlays exceeds max\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
overlays = kmalloc(ovlist.num_overlays * sizeof(*overlays), GFP_KERNEL); | |
if (!overlays) { | |
pr_err("Unable to allocate memory for overlays\n"); | |
return -ENOMEM; | |
} | |
+ if (copy_from_user(req_list, ovlist.overlay_list, | |
+ sizeof(struct mdp_overlay*) * ovlist.num_overlays)) { | |
+ ret = -EFAULT; | |
+ goto validate_exit; | |
+ } | |
+ | |
for (i = 0; i < ovlist.num_overlays; i++) { | |
- if (copy_from_user(overlays + i, ovlist.overlay_list[i], | |
+ if (copy_from_user(overlays + i, req_list[i], | |
sizeof(struct mdp_overlay))) { | |
ret = -EFAULT; | |
goto validate_exit; | |
@@ -2350,7 +2659,7 @@ static int __handle_ioctl_overlay_prepare(struct msm_fb_data_type *mfd, | |
ret = __handle_overlay_prepare(mfd, &ovlist, overlays); | |
if (!IS_ERR_VALUE(ret)) { | |
for (i = 0; i < ovlist.num_overlays; i++) { | |
- if (copy_to_user(ovlist.overlay_list[i], overlays + i, | |
+ if (copy_to_user(req_list[i], overlays + i, | |
sizeof(struct mdp_overlay))) { | |
ret = -EFAULT; | |
goto validate_exit; | |
@@ -2541,6 +2850,13 @@ static struct mdss_mdp_ctl *__mdss_mdp_overlay_ctl_init( | |
mdss_mdp_overlay_handle_vsync; | |
ctl->vsync_handler.cmd_post_flush = false; | |
+ ctl->recover_underrun_handler.vsync_handler = | |
+ mdss_mdp_recover_underrun_handler; | |
+ ctl->recover_underrun_handler.cmd_post_flush = false; | |
+ | |
+ INIT_WORK(&ctl->remove_underrun_handler, | |
+ remove_underrun_vsync_handler); | |
+ | |
if (mfd->split_display && pdata->next) { | |
/* enable split display */ | |
rc = mdss_mdp_ctl_split_display_setup(ctl, pdata->next); | |
@@ -2581,11 +2897,13 @@ static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd) | |
} | |
if (!mfd->panel_info->cont_splash_enabled && | |
- (mfd->panel_info->type != DTV_PANEL) && | |
- (mfd->panel_info->type != WRITEBACK_PANEL)) { | |
+ (mfd->panel_info->type != DTV_PANEL)) { | |
rc = mdss_mdp_overlay_start(mfd); | |
- if (!IS_ERR_VALUE(rc)) | |
+ if (!IS_ERR_VALUE(rc) && | |
+ (mfd->panel_info->type != WRITEBACK_PANEL)) { | |
+ atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt); | |
rc = mdss_mdp_overlay_kickoff(mfd, NULL); | |
+ } | |
} else { | |
rc = mdss_mdp_ctl_setup(mdp5_data->ctl); | |
if (rc) | |
@@ -2632,15 +2950,29 @@ static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd) | |
if (mixer) | |
mixer->cursor_enabled = 0; | |
- mutex_lock(&mfd->lock); | |
+ mutex_lock(&mdp5_data->list_lock); | |
need_cleanup = !list_empty(&mdp5_data->pipes_cleanup); | |
- mutex_unlock(&mfd->lock); | |
+ mutex_unlock(&mdp5_data->list_lock); | |
if (need_cleanup) { | |
pr_debug("cleaning up pipes on fb%d\n", mfd->index); | |
mdss_mdp_overlay_kickoff(mfd, NULL); | |
} | |
+ /* | |
+ * If retire fences are still active wait for a vsync time | |
+ * for retire fence to be updated. | |
+ * As a last resort signal the timeline if vsync doesn't arrive. | |
+ */ | |
+ if (mdp5_data->retire_cnt) { | |
+ u32 fps = mdss_panel_get_framerate(mfd->panel_info); | |
+ u32 vsync_time = 1000 / (fps ? : DEFAULT_FRAME_RATE); | |
+ | |
+ msleep(vsync_time); | |
+ | |
+ __vsync_retire_signal(mfd, mdp5_data->retire_cnt); | |
+ } | |
+ | |
rc = mdss_mdp_ctl_stop(mdp5_data->ctl); | |
if (rc == 0) { | |
__mdss_mdp_overlay_free_list_purge(mfd); | |
@@ -2666,17 +2998,62 @@ static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd) | |
int mdss_panel_register_done(struct mdss_panel_data *pdata) | |
{ | |
- /* | |
- * Clocks are already on if continuous splash is enabled, | |
- * increasing ref_cnt to help balance clocks once done. | |
- */ | |
- if (pdata->panel_info.cont_splash_enabled) { | |
+ if (pdata->panel_info.cont_splash_enabled) | |
mdss_mdp_footswitch_ctrl_splash(1); | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
- } | |
+ | |
return 0; | |
} | |
+static int __mdss_mdp_ctl_handoff(struct mdss_mdp_ctl *ctl, | |
+ struct mdss_data_type *mdata) | |
+{ | |
+ int rc = 0; | |
+ int i, j; | |
+ u32 mixercfg; | |
+ struct mdss_mdp_pipe *pipe = NULL; | |
+ | |
+ if (!ctl || !mdata) | |
+ return -EINVAL; | |
+ | |
+ for (i = 0; i < mdata->nmixers_intf; i++) { | |
+ mixercfg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i)); | |
+ pr_debug("for lm%d mixercfg = 0x%09x\n", i, mixercfg); | |
+ | |
+ j = MDSS_MDP_SSPP_VIG0; | |
+ for (; j < MDSS_MDP_MAX_SSPP && mixercfg; j++) { | |
+ u32 cfg = j * 3; | |
+ if ((j == MDSS_MDP_SSPP_VIG3) || | |
+ (j == MDSS_MDP_SSPP_RGB3)) { | |
+ /* Add 2 to account for Cursor & Border bits */ | |
+ cfg += 2; | |
+ } | |
+ if (mixercfg & (0x7 << cfg)) { | |
+ pr_debug("Pipe %d staged\n", j); | |
+ pipe = mdss_mdp_pipe_search(mdata, BIT(j)); | |
+ if (!pipe) { | |
+ pr_warn("Invalid pipe %d staged\n", j); | |
+ continue; | |
+ } | |
+ | |
+ rc = mdss_mdp_pipe_handoff(pipe); | |
+ if (rc) { | |
+ pr_err("Failed to handoff pipe%d\n", | |
+ pipe->num); | |
+ goto exit; | |
+ } | |
+ | |
+ rc = mdss_mdp_mixer_handoff(ctl, i, pipe); | |
+ if (rc) { | |
+ pr_err("failed to handoff mix%d\n", i); | |
+ goto exit; | |
+ } | |
+ } | |
+ } | |
+ } | |
+exit: | |
+ return rc; | |
+} | |
+ | |
/** | |
* mdss_mdp_overlay_handoff() - Read MDP registers to handoff an active ctl path | |
* @mfd: Msm frame buffer structure associated with the fb device. | |
@@ -2692,10 +3069,8 @@ static int mdss_mdp_overlay_handoff(struct msm_fb_data_type *mfd) | |
int rc = 0; | |
struct mdss_data_type *mdata = mfd_to_mdata(mfd); | |
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
- int i, j; | |
- u32 reg; | |
- struct mdss_mdp_pipe *pipe = NULL; | |
struct mdss_mdp_ctl *ctl = NULL; | |
+ struct mdss_mdp_ctl *sctl = NULL; | |
if (!mdp5_data->ctl) { | |
ctl = __mdss_mdp_overlay_ctl_init(mfd); | |
@@ -2719,38 +3094,23 @@ static int mdss_mdp_overlay_handoff(struct msm_fb_data_type *mfd) | |
ctl->clk_rate = mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC); | |
pr_debug("Set the ctl clock rate to %d Hz\n", ctl->clk_rate); | |
- for (i = 0; i < mdata->nmixers_intf; i++) { | |
- reg = mdss_mdp_ctl_read(ctl, MDSS_MDP_REG_CTL_LAYER(i)); | |
- pr_debug("for lm%d reg = 0x%09x\n", i, reg); | |
- for (j = MDSS_MDP_SSPP_VIG0; j < MDSS_MDP_MAX_SSPP; j++) { | |
- u32 cfg = j * 3; | |
- if ((j == MDSS_MDP_SSPP_VIG3) || | |
- (j == MDSS_MDP_SSPP_RGB3)) { | |
- /* Add 2 to account for Cursor & Border bits */ | |
- cfg += 2; | |
- } | |
- if (reg & (0x7 << cfg)) { | |
- pr_debug("Pipe %d staged\n", j); | |
- pipe = mdss_mdp_pipe_search(mdata, BIT(j)); | |
- if (!pipe) { | |
- pr_warn("Invalid pipe %d staged\n", j); | |
- continue; | |
- } | |
- | |
- rc = mdss_mdp_pipe_handoff(pipe); | |
- if (rc) { | |
- pr_err("Failed to handoff pipe num %d\n" | |
- , pipe->num); | |
- goto error; | |
- } | |
+ rc = __mdss_mdp_ctl_handoff(ctl, mdata); | |
+ if (rc) { | |
+ pr_err("primary ctl handoff failed. rc=%d\n", rc); | |
+ goto error; | |
+ } | |
- rc = mdss_mdp_mixer_handoff(ctl, i, pipe); | |
- if (rc) { | |
- pr_err("failed to handoff mixer num %d\n" | |
- , i); | |
- goto error; | |
- } | |
- } | |
+ if (mfd->split_display) { | |
+ sctl = mdss_mdp_get_split_ctl(ctl); | |
+ if (!sctl) { | |
+ pr_err("cannot get secondary ctl. fail the handoff\n"); | |
+ rc = -EPERM; | |
+ goto error; | |
+ } | |
+ rc = __mdss_mdp_ctl_handoff(sctl, mdata); | |
+ if (rc) { | |
+ pr_err("secondary ctl handoff failed. rc=%d\n", rc); | |
+ goto error; | |
} | |
} | |
@@ -2762,9 +3122,9 @@ static int mdss_mdp_overlay_handoff(struct msm_fb_data_type *mfd) | |
error: | |
if (rc && ctl) { | |
- __mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_RGB); | |
- __mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_VIG); | |
- __mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_DMA); | |
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_RGB); | |
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_VIG); | |
+ mdss_mdp_handoff_cleanup_pipes(mfd, MDSS_MDP_PIPE_TYPE_DMA); | |
mdss_mdp_ctl_destroy(ctl); | |
mdp5_data->ctl = NULL; | |
mdp5_data->handoff = false; | |
@@ -2791,7 +3151,6 @@ static void __vsync_retire_work_handler(struct work_struct *work) | |
{ | |
struct mdss_overlay_private *mdp5_data = | |
container_of(work, typeof(*mdp5_data), retire_work); | |
- struct msm_sync_pt_data *sync_pt_data; | |
if (!mdp5_data->ctl || !mdp5_data->ctl->mfd) | |
return; | |
@@ -2799,12 +3158,18 @@ static void __vsync_retire_work_handler(struct work_struct *work) | |
if (!mdp5_data->ctl->remove_vsync_handler) | |
return; | |
- sync_pt_data = &mdp5_data->ctl->mfd->mdp_sync_pt_data; | |
- mutex_lock(&sync_pt_data->sync_mutex); | |
+ __vsync_retire_signal(mdp5_data->ctl->mfd, 1); | |
+} | |
+ | |
+static void __vsync_retire_signal(struct msm_fb_data_type *mfd, int val) | |
+{ | |
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
+ | |
+ mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex); | |
if (mdp5_data->retire_cnt > 0) { | |
- sw_sync_timeline_inc(mdp5_data->vsync_timeline, 1); | |
+ sw_sync_timeline_inc(mdp5_data->vsync_timeline, val); | |
- mdp5_data->retire_cnt--; | |
+ mdp5_data->retire_cnt -= min(val, mdp5_data->retire_cnt); | |
if (mdp5_data->retire_cnt == 0) { | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
mdp5_data->ctl->remove_vsync_handler(mdp5_data->ctl, | |
@@ -2812,7 +3177,7 @@ static void __vsync_retire_work_handler(struct work_struct *work) | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
} | |
} | |
- mutex_unlock(&sync_pt_data->sync_mutex); | |
+ mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex); | |
} | |
static struct sync_fence * | |
@@ -2838,7 +3203,7 @@ __vsync_retire_get_fence(struct msm_sync_pt_data *sync_pt_data) | |
return ERR_PTR(-EPERM); | |
} | |
- if (mdp5_data->retire_cnt == 0) { | |
+ if (!mdp5_data->vsync_retire_handler.enabled) { | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
rc = ctl->add_vsync_handler(ctl, | |
&mdp5_data->vsync_retire_handler); | |
@@ -2874,6 +3239,27 @@ static int __vsync_retire_setup(struct msm_fb_data_type *mfd) | |
return 0; | |
} | |
+static int mdss_mdp_update_panel_info(struct msm_fb_data_type *mfd, int mode) | |
+{ | |
+ int ret = 0; | |
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
+ struct mdss_mdp_ctl *ctl = mdp5_data->ctl; | |
+ | |
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_DYNAMIC_SWITCH, | |
+ (void *)(unsigned long)mode); | |
+ if (ret) | |
+ pr_err("Dynamic switch to %s mode failed!\n", | |
+ mode ? "command" : "video"); | |
+ /* | |
+ * Destroy current ctrl sturcture as this is | |
+ * going to be re-initialized with the requested mode. | |
+ */ | |
+ mdss_mdp_ctl_destroy(mdp5_data->ctl); | |
+ mdp5_data->ctl = NULL; | |
+ | |
+ return 0; | |
+} | |
+ | |
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) | |
{ | |
struct device *dev = mfd->fbi->dev; | |
@@ -2891,6 +3277,8 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) | |
mdp5_interface->panel_register_done = mdss_panel_register_done; | |
mdp5_interface->kickoff_fnc = mdss_mdp_overlay_kickoff; | |
mdp5_interface->get_sync_fnc = mdss_mdp_rotator_sync_pt_get; | |
+ mdp5_interface->splash_init_fnc = mdss_mdp_splash_init; | |
+ mdp5_interface->configure_panel = mdss_mdp_update_panel_info; | |
mdp5_data = kmalloc(sizeof(struct mdss_overlay_private), GFP_KERNEL); | |
if (!mdp5_data) { | |
@@ -2902,7 +3290,9 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) | |
INIT_LIST_HEAD(&mdp5_data->pipes_used); | |
INIT_LIST_HEAD(&mdp5_data->pipes_cleanup); | |
INIT_LIST_HEAD(&mdp5_data->rot_proc_list); | |
+ mutex_init(&mdp5_data->list_lock); | |
mutex_init(&mdp5_data->ov_lock); | |
+ mutex_init(&mdp5_data->dfps_lock); | |
mdp5_data->hw_refresh = true; | |
mdp5_data->overlay_play_enable = true; | |
@@ -2913,6 +3303,7 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) | |
goto init_fail; | |
} | |
mfd->mdp.private1 = mdp5_data; | |
+ mfd->wait_for_kickoff = true; | |
rc = mdss_mdp_overlay_fb_parse_dt(mfd); | |
if (rc) | |
@@ -2939,7 +3330,10 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) | |
pr_err("Error dfps sysfs creation ret=%d\n", rc); | |
goto init_fail; | |
} | |
- } else if (mfd->panel_info->type == MIPI_CMD_PANEL) { | |
+ } | |
+ | |
+ if (mfd->panel_info->mipi.dynamic_switch_enabled || | |
+ mfd->panel_info->type == MIPI_CMD_PANEL) { | |
rc = __vsync_retire_setup(mfd); | |
if (IS_ERR_VALUE(rc)) { | |
pr_err("unable to create vsync timeline\n"); | |
@@ -2977,51 +3371,14 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) | |
} | |
} | |
+ if (mdss_mdp_pp_overlay_init(mfd)) | |
+ pr_warn("Failed to initialize pp overlay data.\n"); | |
return rc; | |
init_fail: | |
kfree(mdp5_data); | |
return rc; | |
} | |
-static __ref int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd) | |
-{ | |
- struct platform_device *pdev = mfd->pdev; | |
- struct mdss_overlay_private *mdp5_mdata = mfd_to_mdp5_data(mfd); | |
- int len = 0, rc = 0; | |
- u32 offsets[2]; | |
- | |
- of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", &len); | |
- | |
- if (len < 1) { | |
- pr_debug("mem reservation for splash screen fb not present\n"); | |
- rc = -EINVAL; | |
- goto error; | |
- } | |
- | |
- len = len/sizeof(u32); | |
- | |
- rc = of_property_read_u32_array(pdev->dev.of_node, | |
- "qcom,memblock-reserve", offsets, len); | |
- if (rc) { | |
- pr_debug("Error reading mem reserve settings for fb\n"); | |
- goto error; | |
- } | |
- | |
- if (!memblock_is_reserved(offsets[0])) { | |
- pr_debug("failed to reserve memory for fb splash\n"); | |
- rc = -EINVAL; | |
- goto error; | |
- } | |
- | |
- mdp5_mdata->splash_mem_addr = offsets[0]; | |
- mdp5_mdata->splash_mem_size = offsets[1]; | |
- pr_debug("memaddr=%x size=%x\n", mdp5_mdata->splash_mem_addr, | |
- mdp5_mdata->splash_mem_size); | |
- | |
-error: | |
- return rc; | |
-} | |
- | |
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd) | |
{ | |
int rc = 0; | |
@@ -3035,13 +3392,5 @@ static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd) | |
pdev->name); | |
} | |
- rc = mdss_mdp_overlay_splash_parse_dt(mfd); | |
- if (rc && mfd->panel_info->cont_splash_enabled) { | |
- pr_err("No rsvd mem found in DT for splash screen\n"); | |
- } else { | |
- pr_debug("Mem reservation not reqd if cont splash diasbled\n"); | |
- rc = 0; | |
- } | |
- | |
return rc; | |
} | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c | |
index f09f353..134a3d9 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -28,10 +28,16 @@ | |
#define PIPE_HALT_TIMEOUT_US 0x4000 | |
+/* following offsets are relative to ctrl register bit offset */ | |
+#define CLK_FORCE_ON_OFFSET 0x0 | |
+#define CLK_FORCE_OFF_OFFSET 0x1 | |
+/* following offsets are relative to status register bit offset */ | |
+#define CLK_STATUS_OFFSET 0x0 | |
+ | |
static DEFINE_MUTEX(mdss_mdp_sspp_lock); | |
static DEFINE_MUTEX(mdss_mdp_smp_lock); | |
-static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe); | |
+static void mdss_mdp_pipe_free(struct kref *kref); | |
static int mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp); | |
static void mdss_mdp_smp_mmb_free(unsigned long *smp, bool write); | |
static struct mdss_mdp_pipe *mdss_mdp_pipe_search_by_client_id( | |
@@ -129,6 +135,26 @@ static void mdss_mdp_smp_mmb_free(unsigned long *smp, bool write) | |
} | |
} | |
+/** | |
+ * @mdss_mdp_smp_get_size - get allocated smp size for a pipe | |
+ * @pipe: pointer to a pipe | |
+ * | |
+ * Function counts number of blocks that are currently allocated for a | |
+ * pipe, then smp buffer size is number of blocks multiplied by block | |
+ * size. | |
+ */ | |
+u32 mdss_mdp_smp_get_size(struct mdss_mdp_pipe *pipe) | |
+{ | |
+ int i, mb_cnt = 0; | |
+ | |
+ for (i = 0; i < MAX_PLANES; i++) { | |
+ mb_cnt += bitmap_weight(pipe->smp_map[i].allocated, SMP_MB_CNT); | |
+ mb_cnt += bitmap_weight(pipe->smp_map[i].fixed, SMP_MB_CNT); | |
+ } | |
+ | |
+ return mb_cnt * SMP_MB_SIZE; | |
+} | |
+ | |
static void mdss_mdp_smp_set_wm_levels(struct mdss_mdp_pipe *pipe, int mb_cnt) | |
{ | |
u32 fetch_size, val, wm[3]; | |
@@ -194,7 +220,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) | |
u32 nlines, format, seg_w; | |
u16 width; | |
- width = pipe->src.w >> pipe->horz_deci; | |
+ width = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci); | |
if (pipe->bwc_mode) { | |
rc = mdss_mdp_get_rau_strides(pipe->src.w, pipe->src.h, | |
@@ -251,7 +277,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) | |
if (pipe->mixer && pipe->mixer->rotator_mode) { | |
rot_mode = 1; | |
- } else if (ps.num_planes == 1) { | |
+ } else if (pipe->mixer && (ps.num_planes == 1)) { | |
ps.ystride[0] = MAX_BPP * | |
max(pipe->mixer->width, width); | |
} else if (mdata->has_decimation) { | |
@@ -269,7 +295,10 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) | |
} | |
} | |
- nlines = pipe->bwc_mode ? 1 : 2; | |
+ if (pipe->src_fmt->tile) | |
+ nlines = 8; | |
+ else | |
+ nlines = pipe->bwc_mode ? 1 : 2; | |
if (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) | |
wb_mixer = 1; | |
@@ -315,7 +344,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) | |
for (; i >= 0; i--) | |
mdss_mdp_smp_mmb_free(pipe->smp_map[i].reserved, | |
false); | |
- rc = -ENOMEM; | |
+ rc = -ENOBUFS; | |
} | |
mutex_unlock(&mdss_mdp_smp_lock); | |
@@ -455,21 +484,17 @@ int mdss_mdp_smp_handoff(struct mdss_data_type *mdata) | |
void mdss_mdp_pipe_unmap(struct mdss_mdp_pipe *pipe) | |
{ | |
- int tmp; | |
- | |
- tmp = atomic_dec_return(&pipe->ref_cnt); | |
- | |
- WARN(tmp < 0, "Invalid unmap with ref_cnt=%d", tmp); | |
- if (tmp == 0) | |
- mdss_mdp_pipe_free(pipe); | |
+ if (kref_put_mutex(&pipe->kref, mdss_mdp_pipe_free, | |
+ &mdss_mdp_sspp_lock)) { | |
+ WARN(1, "Unexpected free pipe during unmap"); | |
+ mutex_unlock(&mdss_mdp_sspp_lock); | |
+ } | |
} | |
int mdss_mdp_pipe_map(struct mdss_mdp_pipe *pipe) | |
{ | |
- if (!atomic_inc_not_zero(&pipe->ref_cnt)) { | |
- pr_err("attempting to map unallocated pipe (%d)", pipe->num); | |
+ if (!kref_get_unless_zero(&pipe->kref)) | |
return -EINVAL; | |
- } | |
return 0; | |
} | |
@@ -515,7 +540,7 @@ static struct mdss_mdp_pipe *mdss_mdp_pipe_init(struct mdss_mdp_mixer *mixer, | |
for (i = off; i < npipes; i++) { | |
pipe = pipe_pool + i; | |
- if (atomic_cmpxchg(&pipe->ref_cnt, 0, 1) == 0) { | |
+ if (atomic_read(&pipe->kref.refcount) == 0) { | |
pipe->mixer = mixer; | |
break; | |
} | |
@@ -523,9 +548,8 @@ static struct mdss_mdp_pipe *mdss_mdp_pipe_init(struct mdss_mdp_mixer *mixer, | |
} | |
if (pipe && mdss_mdp_pipe_fetch_halt(pipe)) { | |
- pr_err("%d failed because vbif client is in bad state\n", | |
+ pr_err("%d failed because pipe is in bad state\n", | |
pipe->num); | |
- atomic_dec(&pipe->ref_cnt); | |
return NULL; | |
} | |
@@ -533,13 +557,14 @@ static struct mdss_mdp_pipe *mdss_mdp_pipe_init(struct mdss_mdp_mixer *mixer, | |
pr_debug("type=%x pnum=%d\n", pipe->type, pipe->num); | |
mutex_init(&pipe->pp_res.hist.hist_mutex); | |
spin_lock_init(&pipe->pp_res.hist.hist_lock); | |
+ kref_init(&pipe->kref); | |
} else if (pipe_share) { | |
/* | |
* when there is no dedicated wfd blk, DMA pipe can be | |
* shared as long as its attached to a writeback mixer | |
*/ | |
pipe = mdata->dma_pipes + mixer->num; | |
- mdss_mdp_pipe_map(pipe); | |
+ kref_get(&pipe->kref); | |
pr_debug("pipe sharing for pipe=%d\n", pipe->num); | |
} else { | |
pr_err("no %d type pipes available\n", type); | |
@@ -561,7 +586,7 @@ struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_dma(struct mdss_mdp_mixer *mixer) | |
} else if (pipe != &mdata->dma_pipes[mixer->num]) { | |
pr_err("Requested DMA pnum=%d not available\n", | |
mdata->dma_pipes[mixer->num].num); | |
- mdss_mdp_pipe_unmap(pipe); | |
+ kref_put(&pipe->kref, mdss_mdp_pipe_free); | |
pipe = NULL; | |
} else { | |
pipe->mixer = mixer; | |
@@ -648,21 +673,75 @@ struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata, | |
return NULL; | |
} | |
-static int mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe) | |
+static void mdss_mdp_pipe_free(struct kref *kref) | |
{ | |
- pr_debug("ndx=%x pnum=%d ref_cnt=%d\n", pipe->ndx, pipe->num, | |
- atomic_read(&pipe->ref_cnt)); | |
+ struct mdss_mdp_pipe *pipe; | |
+ | |
+ pipe = container_of(kref, struct mdss_mdp_pipe, kref); | |
+ | |
+ pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num); | |
+ | |
+ if (pipe->play_cnt) { | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ mdss_mdp_pipe_fetch_halt(pipe); | |
+ mdss_mdp_pipe_sspp_term(pipe); | |
+ mdss_mdp_smp_free(pipe); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
+ } else { | |
+ mdss_mdp_smp_unreserve(pipe); | |
+ } | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
- mdss_mdp_pipe_sspp_term(pipe); | |
- mdss_mdp_smp_free(pipe); | |
pipe->flags = 0; | |
pipe->bwc_mode = 0; | |
+ pipe->mfd = NULL; | |
memset(&pipe->scale, 0, sizeof(struct mdp_scale_data)); | |
+} | |
+ | |
+static int mdss_mdp_is_pipe_idle(struct mdss_mdp_pipe *pipe, | |
+ bool ignore_force_on) | |
+{ | |
+ u32 reg_val; | |
+ u32 vbif_idle_mask, forced_on_mask, clk_status_idle_mask; | |
+ bool is_idle = false, is_forced_on; | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
+ | |
+ forced_on_mask = BIT(pipe->clk_ctrl.bit_off + CLK_FORCE_ON_OFFSET); | |
+ reg_val = readl_relaxed(mdata->mdp_base + pipe->clk_ctrl.reg_off); | |
+ is_forced_on = (reg_val & forced_on_mask) ? true : false; | |
+ | |
+ pr_debug("pipe#:%d clk_ctrl: 0x%x forced_on_mask: 0x%x\n", pipe->num, | |
+ reg_val, forced_on_mask); | |
+ /* if forced on then no need to check status */ | |
+ if (!is_forced_on) { | |
+ clk_status_idle_mask = | |
+ BIT(pipe->clk_status.bit_off + CLK_STATUS_OFFSET); | |
+ reg_val = readl_relaxed(mdata->mdp_base + | |
+ pipe->clk_status.reg_off); | |
+ | |
+ if (reg_val & clk_status_idle_mask) | |
+ is_idle = false; | |
+ | |
+ pr_debug("pipe#:%d clk_status:0x%x clk_status_idle_mask:0x%x\n", | |
+ pipe->num, reg_val, clk_status_idle_mask); | |
+ } | |
+ | |
+ if (!ignore_force_on && (is_forced_on || !is_idle)) | |
+ goto exit; | |
+ vbif_idle_mask = BIT(pipe->xin_id + 16); | |
+ reg_val = readl_relaxed(mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL1); | |
+ | |
+ if (reg_val & vbif_idle_mask) | |
+ is_idle = true; | |
+ | |
+ pr_debug("pipe#:%d XIN_HALT_CTRL1: 0x%x\n", pipe->num, reg_val); | |
+ | |
+exit: | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
- return 0; | |
+ return is_idle; | |
} | |
/** | |
@@ -684,18 +763,15 @@ int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe) | |
u32 reg_val, idle_mask, status; | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
- | |
- idle_mask = BIT(pipe->xin_id + 16); | |
- reg_val = readl_relaxed(mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL1); | |
- | |
- is_idle = (reg_val & idle_mask) ? true : false; | |
+ is_idle = mdss_mdp_is_pipe_idle(pipe, true); | |
if (!is_idle) { | |
- pr_debug("%pS: pipe%d is not idle. xin_id=%d halt_ctrl1=0x%x\n", | |
- __builtin_return_address(0), pipe->num, pipe->xin_id, | |
- reg_val); | |
+ pr_err("%pS: pipe%d is not idle. xin_id=%d\n", | |
+ __builtin_return_address(0), pipe->num, pipe->xin_id); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
mutex_lock(&mdata->reg_lock); | |
+ idle_mask = BIT(pipe->xin_id + 16); | |
+ | |
reg_val = readl_relaxed(mdata->vbif_base + | |
MMSS_VBIF_XIN_HALT_CTRL0); | |
writel_relaxed(reg_val | BIT(pipe->xin_id), | |
@@ -716,28 +792,26 @@ int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe) | |
MMSS_VBIF_XIN_HALT_CTRL0); | |
writel_relaxed(reg_val & ~BIT(pipe->xin_id), | |
mdata->vbif_base + MMSS_VBIF_XIN_HALT_CTRL0); | |
+ | |
mutex_unlock(&mdata->reg_lock); | |
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
} | |
- mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
return rc; | |
} | |
int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe) | |
{ | |
- int tmp; | |
- | |
- tmp = atomic_dec_return(&pipe->ref_cnt); | |
- | |
- if (tmp != 0) { | |
- pr_err("unable to free pipe %d while still in use (%d)\n", | |
- pipe->num, tmp); | |
+ if (!kref_put_mutex(&pipe->kref, mdss_mdp_pipe_free, | |
+ &mdss_mdp_sspp_lock)) { | |
+ pr_err("unable to free pipe %d while still in use\n", | |
+ pipe->num); | |
return -EBUSY; | |
} | |
- mdss_mdp_pipe_free(pipe); | |
- return 0; | |
+ mutex_unlock(&mdss_mdp_sspp_lock); | |
+ return 0; | |
} | |
/** | |
@@ -795,7 +869,8 @@ int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe) | |
pipe->src_fmt->bpp); | |
pipe->is_handed_off = true; | |
- atomic_inc(&pipe->ref_cnt); | |
+ pipe->play_cnt = 1; | |
+ kref_init(&pipe->kref); | |
error: | |
return rc; | |
@@ -809,9 +884,9 @@ static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe, | |
u32 img_size, src_size, src_xy, dst_size, dst_xy, ystride0, ystride1; | |
u32 width, height; | |
u32 decimation; | |
- struct mdss_rect sci, dst, src; | |
- bool rotation = false; | |
+ struct mdss_mdp_img_rect sci, dst, src; | |
int ret = 0; | |
+ bool rotation = false; | |
pr_debug("pnum=%d wh=%dx%d src={%d,%d,%d,%d} dst={%d,%d,%d,%d}\n", | |
pipe->num, pipe->img_width, pipe->img_height, | |
@@ -852,8 +927,17 @@ static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe, | |
dst = pipe->dst; | |
src = pipe->src; | |
- if (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_INTF) | |
+ if (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_INTF) { | |
mdss_mdp_crop_rect(&src, &dst, &sci); | |
+ if (pipe->flags & MDP_FLIP_LR) { | |
+ src.x = pipe->src.x + (pipe->src.x + pipe->src.w) | |
+ - (src.x + src.w); | |
+ } | |
+ if (pipe->flags & MDP_FLIP_UD) { | |
+ src.y = pipe->src.y + (pipe->src.y + pipe->src.h) | |
+ - (src.y + src.h); | |
+ } | |
+ } | |
src_size = (src.h << 16) | src.w; | |
src_xy = (src.y << 16) | src.x; | |
@@ -909,6 +993,7 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe) | |
u32 chroma_samp, unpack, src_format; | |
u32 secure = 0; | |
u32 opmode; | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
fmt = pipe->src_fmt; | |
@@ -939,6 +1024,9 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe) | |
(fmt->bits[C1_B_Cb] << 2) | | |
(fmt->bits[C0_G_Y] << 0); | |
+ if (fmt->tile) | |
+ src_format |= BIT(30); | |
+ | |
if (pipe->flags & MDP_ROT_90) | |
src_format |= BIT(11); /* ROT90 */ | |
@@ -958,6 +1046,11 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe) | |
if (pipe->scale.enable_pxl_ext) | |
opmode |= (1 << 31); | |
+ if (fmt->tile && mdata->highest_bank_bit) { | |
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_FETCH_CONFIG, | |
+ MDSS_MDP_FETCH_CONFIG_RESET_VALUE | | |
+ mdata->highest_bank_bit << 18); | |
+ } | |
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, src_format); | |
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack); | |
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode); | |
@@ -1033,7 +1126,7 @@ static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe, | |
static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe) | |
{ | |
int ret; | |
- u32 secure, format; | |
+ u32 secure, format, unpack; | |
pr_debug("solid fill setup on pnum=%d\n", pipe->num); | |
@@ -1046,10 +1139,21 @@ static int mdss_mdp_pipe_solidfill_setup(struct mdss_mdp_pipe *pipe) | |
format = MDSS_MDP_FMT_SOLID_FILL; | |
secure = (pipe->flags & MDP_SECURE_OVERLAY_SESSION ? 0xF : 0x0); | |
+ /* support ARGB color format only */ | |
+ unpack = (C3_ALPHA << 24) | (C2_R_Cr << 16) | | |
+ (C1_B_Cb << 8) | (C0_G_Y << 0); | |
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, format); | |
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR, | |
pipe->bg_color); | |
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, unpack); | |
mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_ADDR_SW_STATUS, secure); | |
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, 0); | |
+ | |
+ if (pipe->type != MDSS_MDP_PIPE_TYPE_DMA) { | |
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_CONFIG, 0); | |
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) | |
+ mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE, 0); | |
+ } | |
return 0; | |
} | |
@@ -1146,8 +1250,9 @@ int mdss_mdp_pipe_is_staged(struct mdss_mdp_pipe *pipe) | |
static inline void __mdss_mdp_pipe_program_pixel_extn_helper( | |
struct mdss_mdp_pipe *pipe, u32 plane, u32 off) | |
{ | |
- u32 src_h = pipe->src.h >> pipe->vert_deci; | |
+ u32 src_h = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci); | |
u32 mask = 0xFF; | |
+ u32 lr_pe, tb_pe, tot_req_pixels; | |
/* | |
* CB CR plane required pxls need to be accounted | |
@@ -1155,23 +1260,33 @@ static inline void __mdss_mdp_pipe_program_pixel_extn_helper( | |
*/ | |
if (plane == 1) | |
src_h >>= pipe->chroma_sample_v; | |
- writel_relaxed(((pipe->scale.right_ftch[plane] & mask) << 24)| | |
+ | |
+ lr_pe = ((pipe->scale.right_ftch[plane] & mask) << 24)| | |
((pipe->scale.right_rpt[plane] & mask) << 16)| | |
((pipe->scale.left_ftch[plane] & mask) << 8)| | |
- (pipe->scale.left_rpt[plane] & mask), pipe->base + | |
- MDSS_MDP_REG_SSPP_SW_PIX_EXT_C0_LR + off); | |
- writel_relaxed(((pipe->scale.btm_ftch[plane] & mask) << 24)| | |
+ (pipe->scale.left_rpt[plane] & mask); | |
+ | |
+ tb_pe = ((pipe->scale.btm_ftch[plane] & mask) << 24)| | |
((pipe->scale.btm_rpt[plane] & mask) << 16)| | |
((pipe->scale.top_ftch[plane] & mask) << 8)| | |
- (pipe->scale.top_rpt[plane] & mask), pipe->base + | |
+ (pipe->scale.top_rpt[plane] & mask); | |
+ | |
+ writel_relaxed(lr_pe, pipe->base + | |
+ MDSS_MDP_REG_SSPP_SW_PIX_EXT_C0_LR + off); | |
+ writel_relaxed(tb_pe, pipe->base + | |
MDSS_MDP_REG_SSPP_SW_PIX_EXT_C0_TB + off); | |
+ | |
mask = 0xFFFF; | |
- writel_relaxed((((src_h + pipe->scale.num_ext_pxls_top[plane] + | |
+ tot_req_pixels = (((src_h + pipe->scale.num_ext_pxls_top[plane] + | |
pipe->scale.num_ext_pxls_btm[plane]) & mask) << 16) | | |
((pipe->scale.roi_w[plane] + | |
pipe->scale.num_ext_pxls_left[plane] + | |
- pipe->scale.num_ext_pxls_right[plane]) & mask), pipe->base + | |
+ pipe->scale.num_ext_pxls_right[plane]) & mask); | |
+ writel_relaxed(tot_req_pixels, pipe->base + | |
MDSS_MDP_REG_SSPP_SW_PIX_EXT_C0_REQ_PIXELS + off); | |
+ | |
+ pr_debug("pipe num=%d, plane=%d, LR PE=0x%x, TB PE=0x%x, req_pixels=0x0%x\n", | |
+ pipe->num, plane, lr_pe, tb_pe, tot_req_pixels); | |
} | |
/** | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c | |
index ed004b1..a9e32d7 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c | |
@@ -1,5 +1,5 @@ | |
/* | |
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+ * Copyright (c) 2012-2014, 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 | |
@@ -73,14 +73,36 @@ struct mdp_csc_cfg mdp_csc_convert[MDSS_MDP_MAX_CSC] = { | |
}, | |
}; | |
+/* | |
+ * To program a linear LUT we need to make the slope to be 1/16 to enable | |
+ * conversion from 12bit to 8bit. Also in cases where post blend values might | |
+ * cross 255, we need to cap them now to 255. The offset of the final segment | |
+ * would be programmed in such a case and we set the value to 32460 which is | |
+ * 255 in U8.7. | |
+ */ | |
+static struct mdp_ar_gc_lut_data lin_gc_data[GC_LUT_SEGMENTS] = { | |
+ { 0, 256, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 0}, | |
+ {4095, 0, 0}, {4095, 0, 32640} | |
+}; | |
+ | |
#define CSC_MV_OFF 0x0 | |
#define CSC_BV_OFF 0x2C | |
#define CSC_LV_OFF 0x14 | |
#define CSC_POST_OFF 0xC | |
#define MDSS_BLOCK_DISP_NUM (MDP_BLOCK_MAX - MDP_LOGICAL_BLOCK_DISP_0) | |
+#define MDSS_MAX_MIXER_DISP_NUM (MDSS_BLOCK_DISP_NUM + \ | |
+ MDSS_MDP_WB_MAX_LAYERMIXER) | |
#define HIST_WAIT_TIMEOUT(frame) ((75 * HZ * (frame)) / 1000) | |
+#define HIST_KICKOFF_WAIT_FRACTION 4 | |
+ | |
/* hist collect state */ | |
enum { | |
HIST_UNKNOWN, | |
@@ -181,6 +203,7 @@ static u32 igc_limited[IGC_LUT_ENTRIES] = { | |
#define MDSS_MDP_GAMUT_SIZE 0x5C | |
#define MDSS_MDP_IGC_DSPP_SIZE 0x28 | |
#define MDSS_MDP_IGC_SSPP_SIZE 0x88 | |
+#define MDSS_MDP_VIG_QSEED2_SHARP_SIZE 0x0C | |
#define TOTAL_BLEND_STAGES 0x4 | |
#define PP_FLAGS_DIRTY_PA 0x1 | |
@@ -284,15 +307,15 @@ static struct msm_bus_scale_pdata mdp_pp_bus_scale_table = { | |
struct mdss_pp_res_type { | |
/* logical info */ | |
- u32 pp_disp_flags[MDSS_BLOCK_DISP_NUM]; | |
+ u32 pp_disp_flags[MDSS_MAX_MIXER_DISP_NUM]; | |
u32 igc_lut_c0c1[MDSS_BLOCK_DISP_NUM][IGC_LUT_ENTRIES]; | |
u32 igc_lut_c2[MDSS_BLOCK_DISP_NUM][IGC_LUT_ENTRIES]; | |
struct mdp_ar_gc_lut_data | |
- gc_lut_r[MDSS_BLOCK_DISP_NUM][GC_LUT_SEGMENTS]; | |
+ gc_lut_r[MDSS_MAX_MIXER_DISP_NUM][GC_LUT_SEGMENTS]; | |
struct mdp_ar_gc_lut_data | |
- gc_lut_g[MDSS_BLOCK_DISP_NUM][GC_LUT_SEGMENTS]; | |
+ gc_lut_g[MDSS_MAX_MIXER_DISP_NUM][GC_LUT_SEGMENTS]; | |
struct mdp_ar_gc_lut_data | |
- gc_lut_b[MDSS_BLOCK_DISP_NUM][GC_LUT_SEGMENTS]; | |
+ gc_lut_b[MDSS_MAX_MIXER_DISP_NUM][GC_LUT_SEGMENTS]; | |
u32 enhist_lut[MDSS_BLOCK_DISP_NUM][ENHIST_LUT_ENTRIES]; | |
struct mdp_pa_cfg pa_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
struct mdp_pa_v2_data pa_v2_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
@@ -300,37 +323,18 @@ struct mdss_pp_res_type { | |
u32 six_zone_lut_curve_p1[MDSS_BLOCK_DISP_NUM][MDP_SIX_ZONE_LUT_SIZE]; | |
struct mdp_pcc_cfg_data pcc_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
struct mdp_igc_lut_data igc_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
- struct mdp_pgc_lut_data argc_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
+ struct mdp_pgc_lut_data argc_disp_cfg[MDSS_MAX_MIXER_DISP_NUM]; | |
struct mdp_pgc_lut_data pgc_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
struct mdp_hist_lut_data enhist_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
struct mdp_dither_cfg_data dither_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM]; | |
uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE]; | |
u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE]; | |
- struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM]; | |
+ struct pp_sts_type pp_disp_sts[MDSS_MAX_MIXER_DISP_NUM]; | |
/* physical info */ | |
struct pp_hist_col_info dspp_hist[MDSS_MDP_MAX_DSPP]; | |
}; | |
-#ifdef CONFIG_MACH_LGE | |
-uint32_t igc_Table_RGB[256]={ | |
-4080, 4064, 4048, 4032, 4016, 4000, 3984, 3968, 3952, 3936, 3920, 3904, 3888, 3872, 3856, 3840, 3824, 3808, 3792, 3776, 3760, 3744, 3728, 3712, | |
-3696, 3680, 3664, 3648, 3632, 3616, 3600, 3584, 3568, 3552, 3536, 3520, 3504, 3488, 3472, 3456, 3440, 3424, 3408, 3392, 3376, 3360, 3344, 3328, | |
-3312, 3296, 3280, 3264, 3248, 3232, 3216, 3200, 3184, 3168, 3152, 3136, 3120, 3104, 3088, 3072, 3056, 3040, 3024, 3008, 2992, 2976, 2960, 2944, | |
-2928, 2912, 2896, 2880, 2864, 2848, 2832, 2816, 2800, 2784, 2768, 2752, 2736, 2720, 2704, 2688, 2672, 2656, 2640, 2624, 2608, 2592, 2576, 2560, | |
-2544, 2528, 2512, 2496, 2480, 2464, 2448, 2432, 2416, 2400, 2384, 2368, 2352, 2336, 2320, 2304, 2288, 2272, 2256, 2240, 2224, 2208, 2192, 2176, | |
-2160, 2144, 2128, 2112, 2096, 2080, 2064, 2048, 2032, 2016, 2000, 1984, 1968, 1952, 1936, 1920, 1904, 1888, 1872, 1856, 1840, 1824, 1808, 1792, | |
-1776, 1760, 1744, 1728, 1712, 1696, 1680, 1664, 1648, 1632, 1616, 1600, 1584, 1568, 1552, 1536, 1520, 1504, 1488, 1472, 1456, 1440, 1424, 1408, | |
-1392, 1376, 1360, 1344, 1328, 1312, 1296, 1280, 1264, 1248, 1232, 1216, 1200, 1184, 1168, 1152, 1136, 1120, 1104, 1088, 1072, 1056, 1040, 1024, | |
-1008, 992, 976, 960, 944, 928, 912, 896, 880, 864, 848, 832, 816, 800, 784, 768, 752, 736, 720, 704, 688, 672, 656, 640, | |
- 624, 608, 592, 576, 560, 544, 528, 512, 496, 480, 464, 448, 432, 416, 400, 384, 368, 352, 336, 320, 304, 288, 272, 256, | |
- 240, 224, 208, 192, 176, 160, 144, 128, 112, 96, 80, 64, 48, 32, 16, 0 | |
-}; | |
-int igc_c0_c1[256]={0,}; | |
-int igc_c2[256]={0,}; | |
- | |
-#endif | |
- | |
static DEFINE_MUTEX(mdss_pp_mutex); | |
static struct mdss_pp_res_type *mdss_pp_res; | |
@@ -398,10 +402,11 @@ static int pp_read_pa_v2_regs(char __iomem *addr, | |
u32 disp_num); | |
static void pp_read_pa_mem_col_regs(char __iomem *addr, | |
struct mdp_pa_mem_col_cfg *mem_col_cfg); | |
+static struct msm_fb_data_type *mdss_get_mfd_from_index(int index); | |
static int mdss_ad_init_checks(struct msm_fb_data_type *mfd); | |
static int mdss_mdp_get_ad(struct msm_fb_data_type *mfd, | |
struct mdss_ad_info **ad); | |
-static int pp_update_ad_input(struct msm_fb_data_type *mfd); | |
+static int pp_ad_invalidate_input(struct msm_fb_data_type *mfd); | |
static void pp_ad_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t); | |
static void pp_ad_cfg_write(struct mdss_mdp_ad *ad_hw, | |
struct mdss_ad_info *ad); | |
@@ -415,12 +420,30 @@ static void pp_ad_bypass_config(struct mdss_ad_info *ad, | |
struct mdss_mdp_ctl *ctl, u32 num, u32 *opmode); | |
static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd); | |
static void pp_ad_cfg_lut(char __iomem *addr, u32 *data); | |
+static int pp_ad_attenuate_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out); | |
+static int pp_ad_linearize_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out, | |
+ int inv); | |
+static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, | |
+ bool *bl_out_notify); | |
static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num); | |
static inline bool pp_sts_is_enabled(u32 sts, int side); | |
static inline void pp_sts_set_split_bits(u32 *sts, u32 bits); | |
static u32 last_sts, last_state; | |
+inline int linear_map(int in, int *out, int in_max, int out_max) | |
+{ | |
+ if (in < 0 || !out || in_max <= 0 || out_max <= 0) | |
+ return -EINVAL; | |
+ *out = ((in * out_max) / in_max); | |
+ pr_debug("in = %d, out = %d, in_max = %d, out_max = %d\n", | |
+ in, *out, in_max, out_max); | |
+ if ((in > 0) && (*out == 0)) | |
+ *out = 1; | |
+ return 0; | |
+ | |
+} | |
+ | |
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, u32 tbl_idx, | |
struct mdp_csc_cfg *data) | |
{ | |
@@ -807,9 +830,15 @@ static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op) | |
unsigned long flags = 0; | |
char __iomem *offset; | |
struct mdss_data_type *mdata; | |
+ u32 current_opmode; | |
+ u32 csc_reset; | |
+ u32 dcm_state = DCM_UNINIT; | |
pr_debug("pnum=%x\n", pipe->num); | |
+ if (pipe->mixer && pipe->mixer->ctl && pipe->mixer->ctl->mfd) | |
+ dcm_state = pipe->mixer->ctl->mfd->dcm_state; | |
+ | |
mdata = mdss_mdp_get_mdata(); | |
if ((pipe->flags & MDP_OVERLAY_PP_CFG_EN) && | |
(pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_CSC_CFG)) { | |
@@ -823,19 +852,18 @@ static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op) | |
* TODO: Allow pipe to be programmed whenever new CSC is | |
* applied (i.e. dirty bit) | |
*/ | |
- if (pipe->play_cnt == 0) | |
- mdss_mdp_csc_setup_data(MDSS_MDP_BLOCK_SSPP, | |
- pipe->num, 1, &pipe->pp_cfg.csc_cfg); | |
+ mdss_mdp_csc_setup_data(MDSS_MDP_BLOCK_SSPP, pipe->num, | |
+ 1, &pipe->pp_cfg.csc_cfg); | |
} else { | |
- if (pipe->src_fmt->is_yuv) | |
+ if (pipe->src_fmt->is_yuv) { | |
opmode |= (0 << 19) | /* DST_DATA=RGB */ | |
(1 << 18) | /* SRC_DATA=YCBCR */ | |
(1 << 17); /* CSC_1_EN */ | |
- /* | |
- * TODO: Needs to be part of dirty bit logic: if there is a | |
- * previously configured pipe need to re-configure CSC matrix | |
- */ | |
- if (pipe->play_cnt == 0) { | |
+ /* | |
+ * TODO: Needs to be part of dirty bit logic: if there | |
+ * is a previously configured pipe need to re-configure | |
+ * CSC matrix | |
+ */ | |
mdss_mdp_csc_setup(MDSS_MDP_BLOCK_SSPP, pipe->num, 1, | |
MDSS_MDP_CSC_YUV2RGB); | |
} | |
@@ -843,6 +871,16 @@ static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op) | |
pp_histogram_setup(&opmode, MDSS_PP_SSPP_CFG | pipe->num, pipe->mixer); | |
+ /* Update CSC state only if tuning mode is enable */ | |
+ if (dcm_state == DTM_ENTER) { | |
+ /* Reset bit 16 to 19 for CSC_STATE in VIG_OP_MODE */ | |
+ csc_reset = 0xFFF0FFFF; | |
+ current_opmode = readl_relaxed(pipe->base + | |
+ MDSS_MDP_REG_VIG_OP_MODE); | |
+ *op |= ((current_opmode & csc_reset) | opmode); | |
+ return 0; | |
+ } | |
+ | |
if (pipe->flags & MDP_OVERLAY_PP_CFG_EN) { | |
if ((pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_PA_CFG) && | |
(mdata->mdp_rev < MDSS_MDP_HW_REV_103)) { | |
@@ -928,10 +966,16 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
u32 filter_mode; | |
struct mdss_data_type *mdata; | |
u32 src_w, src_h; | |
+ u32 dcm_state = DCM_UNINIT; | |
+ u32 chroma_shift_x = 0, chroma_shift_y = 0; | |
pr_debug("pipe=%d, change pxl ext=%d\n", pipe->num, | |
pipe->scale.enable_pxl_ext); | |
mdata = mdss_mdp_get_mdata(); | |
+ | |
+ if (pipe->mixer && pipe->mixer->ctl && pipe->mixer->ctl->mfd) | |
+ dcm_state = pipe->mixer->ctl->mfd->dcm_state; | |
+ | |
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_102 && pipe->src_fmt->is_yuv) | |
filter_mode = MDSS_MDP_SCALE_FILTER_CA; | |
else | |
@@ -946,8 +990,8 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
} | |
} | |
- src_w = pipe->src.w >> pipe->horz_deci; | |
- src_h = pipe->src.h >> pipe->vert_deci; | |
+ src_w = DECIMATED_DIMENSION(pipe->src.w, pipe->horz_deci); | |
+ src_h = DECIMATED_DIMENSION(pipe->src.h, pipe->vert_deci); | |
chroma_sample = pipe->src_fmt->chroma_sample; | |
if (pipe->flags & MDP_SOURCE_ROTATED_90) { | |
@@ -966,19 +1010,20 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
pipe->pp_cfg.sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; | |
} | |
- if ((pipe->src_fmt->is_yuv) && | |
- !((pipe->dst.w < src_w) || (pipe->dst.h < src_h))) { | |
- pp_sharp_config(pipe->base + | |
- MDSS_MDP_REG_VIG_QSEED2_SHARP, | |
- &pipe->pp_res.pp_sts, | |
- &pipe->pp_cfg.sharp_cfg); | |
+ if (dcm_state != DTM_ENTER && | |
+ ((pipe->src_fmt->is_yuv) && | |
+ !((pipe->dst.w < src_w) || (pipe->dst.h < src_h)))) { | |
+ pp_sharp_config(pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_SHARP, | |
+ &pipe->pp_res.pp_sts, | |
+ &pipe->pp_cfg.sharp_cfg); | |
} | |
if ((src_h != pipe->dst.h) || | |
(pipe->pp_res.pp_sts.sharp_sts & PP_STS_ENABLE) || | |
(chroma_sample == MDSS_MDP_CHROMA_420) || | |
(chroma_sample == MDSS_MDP_CHROMA_H1V2) || | |
- pipe->scale.enable_pxl_ext) { | |
+ (pipe->scale.enable_pxl_ext && (src_h != pipe->dst.h))) { | |
pr_debug("scale y - src_h=%d dst_h=%d\n", src_h, pipe->dst.h); | |
if ((src_h / MAX_DOWNSCALE_RATIO) > pipe->dst.h) { | |
@@ -992,11 +1037,10 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
init_phasey = pipe->scale.init_phase_y[0]; | |
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { | |
- u32 chroma_shift = 0; | |
if (!pipe->vert_deci && | |
((chroma_sample == MDSS_MDP_CHROMA_420) || | |
(chroma_sample == MDSS_MDP_CHROMA_H1V2))) | |
- chroma_shift = 1; /* 2x upsample chroma */ | |
+ chroma_shift_y = 1; /* 2x upsample chroma */ | |
if (src_h <= pipe->dst.h) | |
scale_config |= /* G/Y, A */ | |
@@ -1007,7 +1051,7 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
(MDSS_MDP_SCALE_FILTER_PCMN << 10) | | |
(MDSS_MDP_SCALE_FILTER_PCMN << 18); | |
- if ((src_h >> chroma_shift) <= pipe->dst.h) | |
+ if ((src_h >> chroma_shift_y) <= pipe->dst.h) | |
scale_config |= /* CrCb */ | |
(MDSS_MDP_SCALE_FILTER_BIL << 14); | |
else | |
@@ -1016,7 +1060,8 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
writel_relaxed(init_phasey, pipe->base + | |
MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY); | |
- writel_relaxed(phasey_step >> chroma_shift, pipe->base + | |
+ writel_relaxed(phasey_step >> chroma_shift_y, | |
+ pipe->base + | |
MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY); | |
} else { | |
if (src_h <= pipe->dst.h) | |
@@ -1034,7 +1079,7 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
(pipe->pp_res.pp_sts.sharp_sts & PP_STS_ENABLE) || | |
(chroma_sample == MDSS_MDP_CHROMA_420) || | |
(chroma_sample == MDSS_MDP_CHROMA_H2V1) || | |
- pipe->scale.enable_pxl_ext) { | |
+ (pipe->scale.enable_pxl_ext && (src_w != pipe->dst.w))) { | |
pr_debug("scale x - src_w=%d dst_w=%d\n", src_w, pipe->dst.w); | |
if ((src_w / MAX_DOWNSCALE_RATIO) > pipe->dst.w) { | |
@@ -1048,12 +1093,10 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
phasex_step = pipe->scale.phase_step_x[0]; | |
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { | |
- u32 chroma_shift = 0; | |
- | |
if (!pipe->horz_deci && | |
((chroma_sample == MDSS_MDP_CHROMA_420) || | |
(chroma_sample == MDSS_MDP_CHROMA_H2V1))) | |
- chroma_shift = 1; /* 2x upsample chroma */ | |
+ chroma_shift_x = 1; /* 2x upsample chroma */ | |
if (src_w <= pipe->dst.w) | |
scale_config |= /* G/Y, A */ | |
@@ -1064,7 +1107,7 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
(MDSS_MDP_SCALE_FILTER_PCMN << 8) | | |
(MDSS_MDP_SCALE_FILTER_PCMN << 16); | |
- if ((src_w >> chroma_shift) <= pipe->dst.w) | |
+ if ((src_w >> chroma_shift_x) <= pipe->dst.w) | |
scale_config |= /* CrCb */ | |
(MDSS_MDP_SCALE_FILTER_BIL << 12); | |
else | |
@@ -1073,7 +1116,8 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
writel_relaxed(init_phasex, pipe->base + | |
MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX); | |
- writel_relaxed(phasex_step >> chroma_shift, pipe->base + | |
+ writel_relaxed(phasex_step >> chroma_shift_x, | |
+ pipe->base + | |
MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX); | |
} else { | |
if (src_w <= pipe->dst.w) | |
@@ -1087,44 +1131,94 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) | |
} | |
} | |
- if (pipe->scale.enable_pxl_ext && | |
- pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { | |
- | |
- /*program x,y initial phase and phase step*/ | |
- writel_relaxed(pipe->scale.init_phase_x[0], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEX); | |
- writel_relaxed(pipe->scale.phase_step_x[0], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPX); | |
- writel_relaxed(pipe->scale.init_phase_x[1], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX); | |
- writel_relaxed(pipe->scale.phase_step_x[1], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX); | |
- | |
- writel_relaxed(pipe->scale.init_phase_y[0], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEY); | |
- writel_relaxed(pipe->scale.phase_step_y[0], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPY); | |
- writel_relaxed(pipe->scale.init_phase_y[1], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY); | |
- writel_relaxed(pipe->scale.phase_step_y[1], | |
- pipe->base + MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY); | |
+ if (pipe->scale.enable_pxl_ext) { | |
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { | |
+ /*program x,y initial phase and phase step*/ | |
+ writel_relaxed(pipe->scale.init_phase_x[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEX); | |
+ writel_relaxed(pipe->scale.phase_step_x[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPX); | |
+ writel_relaxed(pipe->scale.init_phase_x[1], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX); | |
+ writel_relaxed(pipe->scale.phase_step_x[1], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX); | |
+ | |
+ writel_relaxed(pipe->scale.init_phase_y[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEY); | |
+ writel_relaxed(pipe->scale.phase_step_y[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPY); | |
+ writel_relaxed(pipe->scale.init_phase_y[1], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY); | |
+ writel_relaxed(pipe->scale.phase_step_y[1], | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY); | |
+ } else { | |
+ writel_relaxed(pipe->scale.phase_step_x[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_PHASE_STEP_X); | |
+ writel_relaxed(pipe->scale.phase_step_y[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_PHASE_STEP_Y); | |
+ writel_relaxed(pipe->scale.init_phase_x[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_INIT_PHASE_X); | |
+ writel_relaxed(pipe->scale.init_phase_y[0], | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_INIT_PHASE_Y); | |
+ } | |
/*program pixel extn values for the SSPP*/ | |
mdss_mdp_pipe_program_pixel_extn(pipe); | |
- } else if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { | |
- writel_relaxed(phasex_step, pipe->base + | |
- MDSS_MDP_REG_SCALE_PHASE_STEP_X); | |
- writel_relaxed(phasey_step, pipe->base + | |
- MDSS_MDP_REG_SCALE_PHASE_STEP_Y); | |
- writel_relaxed(init_phasex, pipe->base + | |
- MDSS_MDP_REG_SCALE_INIT_PHASE_X); | |
- writel_relaxed(init_phasey, pipe->base + | |
- MDSS_MDP_REG_SCALE_INIT_PHASE_Y); | |
} else { | |
- writel_relaxed(phasex_step, pipe->base + | |
- MDSS_MDP_REG_SCALE_PHASE_STEP_X); | |
- writel_relaxed(phasey_step, pipe->base + | |
- MDSS_MDP_REG_SCALE_PHASE_STEP_Y); | |
+ if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { | |
+ /*program x,y initial phase and phase step*/ | |
+ writel_relaxed(0, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEX); | |
+ writel_relaxed(init_phasex, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX); | |
+ writel_relaxed(phasex_step, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPX); | |
+ writel_relaxed(phasex_step >> chroma_shift_x, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX); | |
+ | |
+ writel_relaxed(0, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEY); | |
+ writel_relaxed(init_phasey, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY); | |
+ writel_relaxed(phasey_step, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPY); | |
+ writel_relaxed(phasey_step >> chroma_shift_y, | |
+ pipe->base + | |
+ MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY); | |
+ } else { | |
+ | |
+ writel_relaxed(phasex_step, | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_PHASE_STEP_X); | |
+ writel_relaxed(phasey_step, | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_PHASE_STEP_Y); | |
+ writel_relaxed(0, | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_INIT_PHASE_X); | |
+ writel_relaxed(0, | |
+ pipe->base + | |
+ MDSS_MDP_REG_SCALE_INIT_PHASE_Y); | |
+ } | |
} | |
writel_relaxed(scale_config, pipe->base + | |
@@ -1155,15 +1249,17 @@ void mdss_mdp_pipe_sspp_term(struct mdss_mdp_pipe *pipe) | |
struct pp_hist_col_info *hist_info; | |
char __iomem *ctl_base; | |
- if (!pipe && pipe->pp_res.hist.col_en) { | |
- done_bit = 3 << (pipe->num * 4); | |
- hist_info = &pipe->pp_res.hist; | |
- ctl_base = pipe->base + | |
- MDSS_MDP_REG_VIG_HIST_CTL_BASE; | |
- pp_histogram_disable(hist_info, done_bit, ctl_base); | |
+ if (pipe) { | |
+ if (pipe->pp_res.hist.col_en) { | |
+ done_bit = 3 << (pipe->num * 4); | |
+ hist_info = &pipe->pp_res.hist; | |
+ ctl_base = pipe->base + | |
+ MDSS_MDP_REG_VIG_HIST_CTL_BASE; | |
+ pp_histogram_disable(hist_info, done_bit, ctl_base); | |
+ } | |
+ memset(&pipe->pp_cfg, 0, sizeof(struct mdp_overlay_pp_params)); | |
+ memset(&pipe->pp_res, 0, sizeof(struct mdss_pipe_pp_res)); | |
} | |
- memset(&pipe->pp_cfg, 0, sizeof(struct mdp_overlay_pp_params)); | |
- memset(&pipe->pp_res, 0, sizeof(struct mdss_pipe_pp_res)); | |
} | |
int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op) | |
@@ -1173,10 +1269,23 @@ int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op) | |
char __iomem *pipe_base; | |
u32 pipe_num; | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ u32 current_opmode; | |
+ u32 dcm_state = DCM_UNINIT; | |
if (pipe == NULL) | |
return -EINVAL; | |
+ if (pipe->mixer && pipe->mixer->ctl && pipe->mixer->ctl->mfd) | |
+ dcm_state = pipe->mixer->ctl->mfd->dcm_state; | |
+ | |
+ /* Read IGC state and update the same if tuning mode is enable */ | |
+ if (dcm_state == DTM_ENTER) { | |
+ current_opmode = readl_relaxed(pipe->base + | |
+ MDSS_MDP_REG_SSPP_SRC_OP_MODE); | |
+ *op |= (current_opmode & BIT(16)); | |
+ return ret; | |
+ } | |
+ | |
/* | |
* TODO: should this function be responsible for masking multiple | |
* pipes to be written in dual pipe case? | |
@@ -1214,44 +1323,69 @@ int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op) | |
static int pp_mixer_setup(u32 disp_num, | |
struct mdss_mdp_mixer *mixer) | |
{ | |
- u32 flags, dspp_num, opmode = 0; | |
+ u32 flags, mixer_num, opmode = 0, lm_bitmask = 0; | |
struct mdp_pgc_lut_data *pgc_config; | |
struct pp_sts_type *pp_sts; | |
struct mdss_mdp_ctl *ctl; | |
char __iomem *addr; | |
- dspp_num = mixer->num; | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
- if (!mixer || !mixer->ctl) | |
+ if (!mixer || !mixer->ctl || !mdata) | |
return -EINVAL; | |
+ | |
+ mixer_num = mixer->num; | |
ctl = mixer->ctl; | |
+ lm_bitmask = (BIT(6) << mixer_num); | |
- /* no corresponding dspp */ | |
- if ((mixer->type != MDSS_MDP_MIXER_TYPE_INTF) || | |
- (dspp_num >= MDSS_MDP_MAX_DSPP)) | |
+ /* Assign appropriate flags after mixer index validation */ | |
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) { | |
+ if (mixer_num >= mdata->nmixers_intf) { | |
+ pr_err("bad intf mixer index = %d total = %d\n", | |
+ mixer_num, mdata->nmixers_intf); | |
+ return 0; | |
+ } | |
+ if (mixer_num == MDSS_MDP_DSPP3) | |
+ lm_bitmask = BIT(20); | |
+ } else if (mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) { | |
+ if (mixer_num >= mdata->nmixers_wb + | |
+ mdata->nmixers_intf) { | |
+ pr_err("bad wb mixer index = %d total = %d\n", | |
+ mixer_num, | |
+ mdata->nmixers_intf + mdata->nmixers_wb); | |
+ return 0; | |
+ } | |
+ } else { | |
return 0; | |
- if (disp_num < MDSS_BLOCK_DISP_NUM) | |
- flags = mdss_pp_res->pp_disp_flags[disp_num]; | |
- else | |
- flags = 0; | |
+ } | |
+ flags = mdss_pp_res->pp_disp_flags[disp_num]; | |
pp_sts = &mdss_pp_res->pp_disp_sts[disp_num]; | |
/* GC_LUT is in layer mixer */ | |
if (flags & PP_FLAGS_DIRTY_ARGC) { | |
pgc_config = &mdss_pp_res->argc_disp_cfg[disp_num]; | |
- if (pgc_config->flags & MDP_PP_OPS_WRITE) { | |
- addr = mixer->base + | |
- MDSS_MDP_REG_LM_GC_LUT_BASE; | |
+ addr = mixer->base + MDSS_MDP_REG_LM_GC_LUT_BASE; | |
+ /* | |
+ * ARGC will always be enabled. When user setting is | |
+ * disabled we program the linear ARGC data to enable | |
+ * rounding in HW. | |
+ */ | |
+ pp_sts->argc_sts |= PP_STS_ENABLE; | |
+ if (pgc_config->flags & MDP_PP_OPS_WRITE) | |
+ pp_update_argc_lut(addr, pgc_config); | |
+ if (pgc_config->flags & MDP_PP_OPS_DISABLE) { | |
+ pgc_config->r_data = &lin_gc_data[0]; | |
+ pgc_config->g_data = &lin_gc_data[0]; | |
+ pgc_config->b_data = &lin_gc_data[0]; | |
+ pgc_config->num_r_stages = GC_LUT_SEGMENTS; | |
+ pgc_config->num_g_stages = GC_LUT_SEGMENTS; | |
+ pgc_config->num_b_stages = GC_LUT_SEGMENTS; | |
pp_update_argc_lut(addr, pgc_config); | |
} | |
- if (pgc_config->flags & MDP_PP_OPS_DISABLE) | |
- pp_sts->argc_sts &= ~PP_STS_ENABLE; | |
- else if (pgc_config->flags & MDP_PP_OPS_ENABLE) | |
- pp_sts->argc_sts |= PP_STS_ENABLE; | |
- ctl->flush_bits |= BIT(6) << dspp_num; /* LAYER_MIXER */ | |
+ ctl->flush_bits |= lm_bitmask; | |
} | |
+ | |
/* update LM opmode if LM needs flush */ | |
- if ((pp_sts->argc_sts & PP_STS_ENABLE) && | |
- (ctl->flush_bits & (BIT(6) << dspp_num))) { | |
+ if (flags & PP_FLAGS_DIRTY_ARGC) { | |
addr = mixer->base + MDSS_MDP_REG_LM_OP_MODE; | |
opmode = readl_relaxed(addr); | |
opmode |= (1 << 0); /* GC_LUT_EN */ | |
@@ -1324,19 +1458,20 @@ static int pp_histogram_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix) | |
goto error; | |
} | |
+ mutex_lock(&hist_info->hist_mutex); | |
+ spin_lock_irqsave(&hist_info->hist_lock, flag); | |
if (hist_info->col_en) { | |
*op |= op_flags; | |
- mutex_lock(&hist_info->hist_mutex); | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
col_state = hist_info->col_state; | |
if (col_state == HIST_IDLE) { | |
/* Kick off collection */ | |
writel_relaxed(1, base + kick_base); | |
hist_info->col_state = HIST_START; | |
+ complete(&hist_info->first_kick); | |
} | |
- spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
- mutex_unlock(&hist_info->hist_mutex); | |
} | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
+ mutex_unlock(&hist_info->hist_mutex); | |
ret = 0; | |
error: | |
return ret; | |
@@ -1487,23 +1622,23 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) | |
pp_sts = &mdss_pp_res->pp_disp_sts[disp_num]; | |
- if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) { | |
- pp_pa_v2_config(flags, base + MDSS_MDP_REG_DSPP_PA_BASE, pp_sts, | |
- &mdss_pp_res->pa_v2_disp_cfg[disp_num], | |
- PP_DSPP); | |
- } else | |
- pp_pa_config(flags, base + MDSS_MDP_REG_DSPP_PA_BASE, pp_sts, | |
- &mdss_pp_res->pa_disp_cfg[disp_num]); | |
- | |
- pp_pcc_config(flags, base + MDSS_MDP_REG_DSPP_PCC_BASE, pp_sts, | |
- &mdss_pp_res->pcc_disp_cfg[disp_num]); | |
+ if (disp_num < MDSS_BLOCK_DISP_NUM) { | |
+ if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) { | |
+ pp_pa_v2_config(flags, base + MDSS_MDP_REG_DSPP_PA_BASE, pp_sts, | |
+ &mdss_pp_res->pa_v2_disp_cfg[disp_num], | |
+ PP_DSPP); | |
+ } else | |
+ pp_pa_config(flags, base + MDSS_MDP_REG_DSPP_PA_BASE, pp_sts, | |
+ &mdss_pp_res->pa_disp_cfg[disp_num]); | |
- pp_igc_config(flags, mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE, | |
+ pp_pcc_config(flags, base + MDSS_MDP_REG_DSPP_PCC_BASE, pp_sts, | |
+ &mdss_pp_res->pcc_disp_cfg[disp_num]); | |
+ pp_igc_config(flags, mdata->mdp_base + MDSS_MDP_REG_IGC_DSPP_BASE, | |
pp_sts, &mdss_pp_res->igc_disp_cfg[disp_num], | |
dspp_num); | |
- | |
- pp_enhist_config(flags, base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE, | |
- pp_sts, &mdss_pp_res->enhist_disp_cfg[disp_num]); | |
+ pp_enhist_config(flags, base + MDSS_MDP_REG_DSPP_HIST_LUT_BASE, | |
+ pp_sts, &mdss_pp_res->enhist_disp_cfg[disp_num]); | |
+ } | |
if (pp_sts->enhist_sts & PP_STS_ENABLE && | |
!(pp_sts->pa_sts & PP_STS_ENABLE)) { | |
@@ -1514,26 +1649,29 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer) | |
writel_relaxed(0, addr + 8); | |
writel_relaxed(0, addr + 12); | |
} | |
- if (flags & PP_FLAGS_DIRTY_DITHER) { | |
- addr = base + MDSS_MDP_REG_DSPP_DITHER_DEPTH; | |
- pp_dither_config(addr, pp_sts, | |
- &mdss_pp_res->dither_disp_cfg[disp_num]); | |
- } | |
- if (flags & PP_FLAGS_DIRTY_GAMUT) | |
- pp_gamut_config(&mdss_pp_res->gamut_disp_cfg[disp_num], base, | |
- pp_sts); | |
- if (flags & PP_FLAGS_DIRTY_PGC) { | |
- pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; | |
- if (pgc_config->flags & MDP_PP_OPS_WRITE) { | |
- addr = base + MDSS_MDP_REG_DSPP_GC_BASE; | |
- pp_update_argc_lut(addr, pgc_config); | |
+ if (disp_num < MDSS_BLOCK_DISP_NUM) { | |
+ if (flags & PP_FLAGS_DIRTY_DITHER) { | |
+ addr = base + MDSS_MDP_REG_DSPP_DITHER_DEPTH; | |
+ pp_dither_config(addr, pp_sts, | |
+ &mdss_pp_res->dither_disp_cfg[disp_num]); | |
+ } | |
+ if (flags & PP_FLAGS_DIRTY_GAMUT) | |
+ pp_gamut_config(&mdss_pp_res->gamut_disp_cfg[disp_num], base, | |
+ pp_sts); | |
+ | |
+ if (flags & PP_FLAGS_DIRTY_PGC) { | |
+ pgc_config = &mdss_pp_res->pgc_disp_cfg[disp_num]; | |
+ if (pgc_config->flags & MDP_PP_OPS_WRITE) { | |
+ addr = base + MDSS_MDP_REG_DSPP_GC_BASE; | |
+ pp_update_argc_lut(addr, pgc_config); | |
+ } | |
+ if (pgc_config->flags & MDP_PP_OPS_DISABLE) | |
+ pp_sts->pgc_sts &= ~PP_STS_ENABLE; | |
+ else if (pgc_config->flags & MDP_PP_OPS_ENABLE) | |
+ pp_sts->pgc_sts |= PP_STS_ENABLE; | |
+ pp_sts_set_split_bits(&pp_sts->pgc_sts, pgc_config->flags); | |
} | |
- if (pgc_config->flags & MDP_PP_OPS_DISABLE) | |
- pp_sts->pgc_sts &= ~PP_STS_ENABLE; | |
- else if (pgc_config->flags & MDP_PP_OPS_ENABLE) | |
- pp_sts->pgc_sts |= PP_STS_ENABLE; | |
- pp_sts_set_split_bits(&pp_sts->pgc_sts, pgc_config->flags); | |
} | |
pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev, &opmode); | |
@@ -1570,18 +1708,18 @@ int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl) | |
/* TODO: have some sort of reader/writer lock to prevent unclocked | |
* access while display power is toggled */ | |
- if (!ctl->mfd->panel_power_on) { | |
+ mutex_lock(&ctl->lock); | |
+ if (!ctl->power_on) { | |
ret = -EPERM; | |
goto error; | |
} | |
- mutex_lock(&ctl->mfd->lock); | |
ret = mdss_mdp_pp_setup_locked(ctl); | |
- mutex_unlock(&ctl->mfd->lock); | |
error: | |
+ mutex_unlock(&ctl->lock); | |
+ | |
return ret; | |
} | |
-/* call only when holding and mfd->lock */ | |
int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) | |
{ | |
struct mdss_data_type *mdata = ctl->mdata; | |
@@ -1591,27 +1729,42 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) | |
u32 disp_num; | |
int i; | |
bool valid_mixers = true; | |
+ bool valid_ad_panel = true; | |
if ((!ctl->mfd) || (!mdss_pp_res)) | |
return -EINVAL; | |
/* treat fb_num the same as block logical id*/ | |
disp_num = ctl->mfd->index; | |
+ if (disp_num >= MDSS_MAX_MIXER_DISP_NUM) { | |
+ pr_warn("Invalid display number found, %u", disp_num); | |
+ return -EINVAL; | |
+ } | |
mixer_cnt = mdss_mdp_get_ctl_mixers(disp_num, mixer_id); | |
if (!mixer_cnt) { | |
valid_mixers = false; | |
- ret = -EINVAL; | |
- pr_warn("Configuring post processing without mixers, err = %d", | |
- ret); | |
- goto exit; | |
+ /* exit if mixer is not writeback */ | |
+ if (!ctl->mixer_left || | |
+ (ctl->mixer_left->type == MDSS_MDP_MIXER_TYPE_INTF)) { | |
+ ret = -EINVAL; | |
+ pr_warn("No mixers for post processing err = %d\n", | |
+ ret); | |
+ goto exit; | |
+ } | |
} | |
if (mdata->nad_cfgs == 0) | |
valid_mixers = false; | |
for (i = 0; i < mixer_cnt && valid_mixers; i++) { | |
- if (mixer_id[i] > mdata->nad_cfgs) | |
+ if (mixer_id[i] >= mdata->nad_cfgs) | |
valid_mixers = false; | |
} | |
- if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw)) { | |
+ valid_ad_panel = (ctl->mfd->panel_info->type != DTV_PANEL) && | |
+ (((mdata->mdp_rev < MDSS_MDP_HW_REV_103) && | |
+ (ctl->mfd->panel_info->type == WRITEBACK_PANEL)) || | |
+ (ctl->mfd->panel_info->type != WRITEBACK_PANEL)); | |
+ | |
+ if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw) && | |
+ valid_ad_panel) { | |
ret = mdss_mdp_ad_setup(ctl->mfd); | |
if (ret < 0) | |
pr_warn("ad_setup(disp%d) returns %d", disp_num, ret); | |
@@ -1627,7 +1780,7 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl) | |
pp_dspp_setup(disp_num, ctl->mixer_right); | |
} | |
/* clear dirty flag */ | |
- if (disp_num < MDSS_BLOCK_DISP_NUM) { | |
+ if (disp_num < MDSS_MAX_MIXER_DISP_NUM) { | |
mdss_pp_res->pp_disp_flags[disp_num] = 0; | |
if (disp_num < mdata->nad_cfgs) | |
mdata->ad_cfgs[disp_num].reg_sts = 0; | |
@@ -1643,38 +1796,16 @@ exit: | |
*/ | |
int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) | |
{ | |
- u32 flags = 0, disp_num, bl; | |
+ u32 flags = 0, disp_num, bl, ret = 0; | |
struct pp_sts_type pp_sts; | |
struct mdss_ad_info *ad; | |
struct mdss_data_type *mdata = ctl->mdata; | |
+ struct msm_fb_data_type *bl_mfd; | |
if (dspp_num >= MDSS_MDP_MAX_DSPP) { | |
pr_warn("invalid dspp_num"); | |
return -EINVAL; | |
} | |
disp_num = ctl->mfd->index; | |
- | |
- if (dspp_num < mdata->nad_cfgs) { | |
- ad = &mdata->ad_cfgs[dspp_num]; | |
- | |
- if (PP_AD_STATE_CFG & ad->state) | |
- pp_ad_cfg_write(&mdata->ad_off[dspp_num], ad); | |
- if (PP_AD_STATE_INIT & ad->state) | |
- pp_ad_init_write(&mdata->ad_off[dspp_num], ad, ctl); | |
- if ((PP_AD_STATE_DATA & ad->state) && | |
- (ad->sts & PP_STS_ENABLE)) { | |
- bl = ad->bl_mfd->bl_level; | |
- ad->last_bl = bl; | |
- if (ad->state & PP_AD_STATE_BL_LIN) { | |
- bl = ad->bl_lin[bl >> ad->bl_bright_shift]; | |
- bl = bl << ad->bl_bright_shift; | |
- } | |
- ad->bl_data = bl; | |
- pp_ad_input_write(&mdata->ad_off[dspp_num], ad); | |
- } | |
- if ((PP_AD_STATE_VSYNC & ad->state) && ad->calc_itr) | |
- ctl->add_vsync_handler(ctl, &ad->handle); | |
- } | |
- | |
pp_sts = mdss_pp_res->pp_disp_sts[disp_num]; | |
if (pp_sts.pa_sts & PP_STS_ENABLE) { | |
@@ -1742,6 +1873,44 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) | |
} | |
mdss_pp_res->pp_disp_flags[disp_num] |= flags; | |
+ | |
+ if (dspp_num < mdata->nad_cfgs) { | |
+ ret = mdss_mdp_get_ad(ctl->mfd, &ad); | |
+ if (ret) { | |
+ pr_warn("Failed to get AD info, err = %d\n", ret); | |
+ return ret; | |
+ } | |
+ if (ctl->mfd->panel_info->type == WRITEBACK_PANEL) { | |
+ bl_mfd = mdss_get_mfd_from_index(0); | |
+ if (!bl_mfd) { | |
+ ret = -EINVAL; | |
+ pr_warn("Failed to get primary FB bl handle, err = %d\n", | |
+ ret); | |
+ return ret; | |
+ } | |
+ } else { | |
+ bl_mfd = ctl->mfd; | |
+ } | |
+ | |
+ mutex_lock(&ad->lock); | |
+ bl = bl_mfd->ad_bl_level; | |
+ if (PP_AD_STATE_CFG & ad->state) | |
+ pp_ad_cfg_write(&mdata->ad_off[dspp_num], ad); | |
+ if (PP_AD_STATE_INIT & ad->state) | |
+ pp_ad_init_write(&mdata->ad_off[dspp_num], ad, ctl); | |
+ if ((PP_AD_STATE_DATA & ad->state) && | |
+ (ad->sts & PP_STS_ENABLE)) { | |
+ ad->last_bl = bl; | |
+ linear_map(bl, &ad->bl_data, | |
+ ad->bl_mfd->panel_info->bl_max, | |
+ MDSS_MDP_AD_BL_SCALE); | |
+ pp_ad_input_write(&mdata->ad_off[dspp_num], ad); | |
+ } | |
+ if ((PP_AD_STATE_VSYNC & ad->state) && ad->calc_itr) | |
+ ctl->add_vsync_handler(ctl, &ad->handle); | |
+ mutex_unlock(&ad->lock); | |
+ } | |
+ | |
return 0; | |
} | |
@@ -1751,6 +1920,10 @@ int mdss_mdp_pp_init(struct device *dev) | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
struct mdss_mdp_pipe *vig; | |
struct msm_bus_scale_pdata *pp_bus_pdata; | |
+ struct mdp_pgc_lut_data *gc_cfg; | |
+ | |
+ if (!mdata) | |
+ return -EPERM; | |
mutex_lock(&mdss_pp_mutex); | |
if (!mdss_pp_res) { | |
@@ -1765,14 +1938,31 @@ int mdss_mdp_pp_init(struct device *dev) | |
&mdss_pp_res->dspp_hist[i].hist_mutex); | |
spin_lock_init( | |
&mdss_pp_res->dspp_hist[i].hist_lock); | |
+ init_completion( | |
+ &mdss_pp_res->dspp_hist[i].comp); | |
+ init_completion( | |
+ &mdss_pp_res->dspp_hist[i].first_kick); | |
+ } | |
+ | |
+ /* | |
+ * Set LM ARGC flags to disable. This would program | |
+ * default GC which would allow for rounding in HW. | |
+ */ | |
+ for (i = 0; i < MDSS_MAX_MIXER_DISP_NUM; i++) { | |
+ gc_cfg = &mdss_pp_res->argc_disp_cfg[i]; | |
+ gc_cfg->flags = MDP_PP_OPS_DISABLE; | |
+ mdss_pp_res->pp_disp_flags[i] |= | |
+ PP_FLAGS_DIRTY_ARGC; | |
} | |
} | |
} | |
- if (mdata) { | |
+ if (mdata && mdata->vig_pipes) { | |
vig = mdata->vig_pipes; | |
for (i = 0; i < mdata->nvig_pipes; i++) { | |
mutex_init(&vig[i].pp_res.hist.hist_mutex); | |
spin_lock_init(&vig[i].pp_res.hist.hist_lock); | |
+ init_completion(&vig[i].pp_res.hist.comp); | |
+ init_completion(&vig[i].pp_res.hist.first_kick); | |
} | |
if (!mdata->pp_bus_hdl) { | |
pp_bus_pdata = &mdp_pp_bus_scale_table; | |
@@ -1795,6 +1985,7 @@ int mdss_mdp_pp_init(struct device *dev) | |
mutex_unlock(&mdss_pp_mutex); | |
return ret; | |
} | |
+ | |
void mdss_mdp_pp_term(struct device *dev) | |
{ | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
@@ -1809,6 +2000,89 @@ void mdss_mdp_pp_term(struct device *dev) | |
mutex_unlock(&mdss_pp_mutex); | |
} | |
} | |
+int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) { | |
+ pr_err("Invalid mfd.\n"); | |
+ return -EPERM; | |
+ } | |
+ | |
+ mfd->mdp.ad_calc_bl = pp_ad_calc_bl; | |
+ return 0; | |
+} | |
+ | |
+static int pp_ad_calc_bl(struct msm_fb_data_type *mfd, int bl_in, int *bl_out, | |
+ bool *bl_out_notify) | |
+{ | |
+ int ret = -1; | |
+ int temp = bl_in; | |
+ u32 ad_bl_out = 0; | |
+ struct mdss_ad_info *ad; | |
+ | |
+ ret = mdss_mdp_get_ad(mfd, &ad); | |
+ if (ret == -ENODEV) { | |
+ pr_debug("AD not supported on device.\n"); | |
+ return ret; | |
+ } else if (ret || !ad) { | |
+ pr_err("Failed to get ad info: ret = %d, ad = 0x%p.\n", | |
+ ret, ad); | |
+ return ret; | |
+ } | |
+ | |
+ mutex_lock(&ad->lock); | |
+ if (!(ad->state & PP_AD_STATE_RUN)) { | |
+ pr_debug("AD is not running.\n"); | |
+ mutex_unlock(&ad->lock); | |
+ return -EPERM; | |
+ } | |
+ | |
+ if (!ad->bl_mfd || !ad->bl_mfd->panel_info || | |
+ !ad->bl_att_lut) { | |
+ pr_err("Invalid ad info: bl_mfd = 0x%p, ad->bl_mfd->panel_info = 0x%p, bl_att_lut = 0x%p\n", | |
+ ad->bl_mfd, | |
+ (!ad->bl_mfd) ? NULL : ad->bl_mfd->panel_info, | |
+ ad->bl_att_lut); | |
+ mutex_unlock(&ad->lock); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ ret = pp_ad_linearize_bl(ad, bl_in, &temp, | |
+ MDP_PP_AD_BL_LINEAR); | |
+ if (ret) { | |
+ pr_err("Failed to linearize BL: %d\n", ret); | |
+ mutex_unlock(&ad->lock); | |
+ return ret; | |
+ } | |
+ | |
+ ret = pp_ad_attenuate_bl(ad, temp, &temp); | |
+ if (ret) { | |
+ pr_err("Failed to attenuate BL: %d\n", ret); | |
+ mutex_unlock(&ad->lock); | |
+ return ret; | |
+ } | |
+ ad_bl_out = temp; | |
+ | |
+ ret = pp_ad_linearize_bl(ad, temp, &temp, MDP_PP_AD_BL_LINEAR_INV); | |
+ if (ret) { | |
+ pr_err("Failed to inverse linearize BL: %d\n", ret); | |
+ mutex_unlock(&ad->lock); | |
+ return ret; | |
+ } | |
+ *bl_out = temp; | |
+ | |
+ if(!mfd->ad_bl_level) | |
+ mfd->ad_bl_level = bl_in; | |
+ | |
+ if (ad_bl_out != mfd->ad_bl_level) { | |
+ mfd->ad_bl_level = ad_bl_out; | |
+ *bl_out_notify = true; | |
+ } | |
+ | |
+ pp_ad_invalidate_input(mfd); | |
+ mutex_unlock(&ad->lock); | |
+ return 0; | |
+} | |
+ | |
static int pp_get_dspp_num(u32 disp_num, u32 *dspp_num) | |
{ | |
int i; | |
@@ -2220,51 +2494,19 @@ pcc_config_exit: | |
return ret; | |
} | |
-#ifdef CONFIG_MACH_LGE | |
-int mdss_dsi_panel_invert(u32 enable) | |
+static void pp_read_igc_lut_cached(struct mdp_igc_lut_data *cfg) | |
{ | |
int i; | |
- int disp_num = 0; | |
- struct mdss_mdp_ctl *ctl; | |
- struct mdss_mdp_ctl *ctl_d = NULL; | |
- struct mdss_data_type *mdata; | |
- struct mdp_igc_lut_data *igc_data; | |
+ u32 disp_num; | |
- mdata = mdss_mdp_get_mdata(); | |
- for (i = 0; i < mdata->nctl; i++) { | |
- ctl = mdata->ctl_off + i; | |
- if ((ctl->power_on) && (ctl->mfd) && | |
- (ctl->mfd->index == 0)) { | |
- ctl_d = ctl; | |
- break; | |
- } | |
+ disp_num = cfg->block - MDP_LOGICAL_BLOCK_DISP_0; | |
+ for (i = 0; i < IGC_LUT_ENTRIES; i++) { | |
+ cfg->c0_c1_data[i] = | |
+ mdss_pp_res->igc_disp_cfg[disp_num].c0_c1_data[i]; | |
+ cfg->c2_data[i] = | |
+ mdss_pp_res->igc_disp_cfg[disp_num].c2_data[i]; | |
} | |
- | |
- igc_data = &mdss_pp_res->igc_disp_cfg[disp_num]; | |
- igc_data->c0_c1_data = &mdss_pp_res->igc_lut_c0c1[disp_num][0]; | |
- igc_data->c2_data = &mdss_pp_res->igc_lut_c2[disp_num][0]; | |
- igc_data->block = MDP_LOGICAL_BLOCK_DISP_0; | |
- igc_data->len = 256; | |
- | |
- if (ctl_d && enable) { | |
- igc_data->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_ENABLE; | |
- for (i=0; i<256 ; i++) { | |
- igc_c0_c1[i]=(igc_Table_RGB[i]&0xFFF)|((igc_Table_RGB[i]&0xFFF))<<16; | |
- igc_c2[i]=igc_Table_RGB[i]; | |
- } | |
- igc_data->c0_c1_data=&igc_c0_c1[0]; | |
- igc_data->c2_data=&igc_c2[0]; | |
- } else if(ctl_d && !enable) { | |
- igc_data->ops = MDP_PP_OPS_WRITE | MDP_PP_OPS_DISABLE; | |
- } else { | |
- pr_info("!!!!!!!!!!!!!!!!! null !!!!!!!!!!!!\n"); | |
- } | |
- | |
- mdss_pp_res->pp_disp_flags[disp_num] |= PP_FLAGS_DIRTY_IGC; | |
- | |
- return 0; | |
} | |
-#endif | |
static void pp_read_igc_lut(struct mdp_igc_lut_data *cfg, | |
char __iomem *addr, u32 blk_idx) | |
@@ -2386,14 +2628,17 @@ int mdss_mdp_igc_lut_config(struct mdp_igc_lut_data *config, | |
&mdss_pp_res->igc_lut_c0c1[disp_num][0]; | |
local_cfg.c2_data = | |
&mdss_pp_res->igc_lut_c2[disp_num][0]; | |
- pp_read_igc_lut(&local_cfg, igc_addr, dspp_num); | |
- if (copy_to_user(config->c0_c1_data, local_cfg.c2_data, | |
+ if (mdata->has_no_lut_read) | |
+ pp_read_igc_lut_cached(&local_cfg); | |
+ else | |
+ pp_read_igc_lut(&local_cfg, igc_addr, dspp_num); | |
+ if (copy_to_user(config->c0_c1_data, local_cfg.c0_c1_data, | |
config->len * sizeof(u32))) { | |
ret = -EFAULT; | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
goto igc_config_exit; | |
} | |
- if (copy_to_user(config->c2_data, local_cfg.c0_c1_data, | |
+ if (copy_to_user(config->c2_data, local_cfg.c2_data, | |
config->len * sizeof(u32))) { | |
ret = -EFAULT; | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
@@ -2533,6 +2778,41 @@ static int pp_read_argc_lut(struct mdp_pgc_lut_data *config, char __iomem *addr) | |
return ret; | |
} | |
+static int pp_read_argc_lut_cached(struct mdp_pgc_lut_data *config) | |
+{ | |
+ int i; | |
+ u32 disp_num; | |
+ struct mdp_pgc_lut_data *pgc_ptr; | |
+ | |
+ disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0; | |
+ switch (PP_LOCAT(config->block)) { | |
+ case MDSS_PP_LM_CFG: | |
+ pgc_ptr = &mdss_pp_res->argc_disp_cfg[disp_num]; | |
+ break; | |
+ case MDSS_PP_DSPP_CFG: | |
+ pgc_ptr = &mdss_pp_res->pgc_disp_cfg[disp_num]; | |
+ break; | |
+ default: | |
+ return -EINVAL; | |
+ } | |
+ | |
+ for (i = 0; i < GC_LUT_SEGMENTS; i++) { | |
+ config->r_data[i].x_start = pgc_ptr->r_data[i].x_start; | |
+ config->r_data[i].slope = pgc_ptr->r_data[i].slope; | |
+ config->r_data[i].offset = pgc_ptr->r_data[i].offset; | |
+ | |
+ config->g_data[i].x_start = pgc_ptr->g_data[i].x_start; | |
+ config->g_data[i].slope = pgc_ptr->g_data[i].slope; | |
+ config->g_data[i].offset = pgc_ptr->g_data[i].offset; | |
+ | |
+ config->b_data[i].x_start = pgc_ptr->b_data[i].x_start; | |
+ config->b_data[i].slope = pgc_ptr->b_data[i].slope; | |
+ config->b_data[i].offset = pgc_ptr->b_data[i].offset; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
/* Note: Assumes that its inputs have been checked by calling function */ | |
static void pp_update_hist_lut(char __iomem *addr, | |
struct mdp_hist_lut_data *cfg) | |
@@ -2556,6 +2836,10 @@ int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config, | |
struct mdp_pgc_lut_data *pgc_ptr; | |
u32 tbl_size, r_size, g_size, b_size; | |
char __iomem *argc_addr = 0; | |
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
+ | |
+ if (mdata == NULL) | |
+ return -EPERM; | |
if ((PP_BLOCK(config->block) < MDP_LOGICAL_BLOCK_DISP_0) || | |
(PP_BLOCK(config->block) >= MDP_BLOCK_MAX)) | |
@@ -2569,6 +2853,12 @@ int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config, | |
mutex_lock(&mdss_pp_mutex); | |
disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0; | |
+ ret = pp_get_dspp_num(disp_num, &dspp_num); | |
+ if (ret) { | |
+ pr_err("%s, no dspp connects to disp %d", __func__, disp_num); | |
+ goto argc_config_exit; | |
+ } | |
+ | |
switch (PP_LOCAT(config->block)) { | |
case MDSS_PP_LM_CFG: | |
argc_addr = mdss_mdp_get_mixer_addr_off(dspp_num) + | |
@@ -2594,12 +2884,6 @@ int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config, | |
tbl_size = GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data); | |
if (config->flags & MDP_PP_OPS_READ) { | |
- ret = pp_get_dspp_num(disp_num, &dspp_num); | |
- if (ret) { | |
- pr_err("%s, no dspp connects to disp %d", | |
- __func__, disp_num); | |
- goto argc_config_exit; | |
- } | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
local_cfg = *config; | |
local_cfg.r_data = | |
@@ -2608,21 +2892,31 @@ int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config, | |
&mdss_pp_res->gc_lut_g[disp_num][0]; | |
local_cfg.b_data = | |
&mdss_pp_res->gc_lut_b[disp_num][0]; | |
- pp_read_argc_lut(&local_cfg, argc_addr); | |
- if (copy_to_user(config->r_data, | |
- &mdss_pp_res->gc_lut_r[disp_num][0], tbl_size)) { | |
+ if (mdata->has_no_lut_read) | |
+ pp_read_argc_lut_cached(&local_cfg); | |
+ else | |
+ pp_read_argc_lut(&local_cfg, argc_addr); | |
+ | |
+ if ((tbl_size != local_cfg.num_r_stages * | |
+ sizeof(struct mdp_ar_gc_lut_data)) || | |
+ (copy_to_user(config->r_data, local_cfg.r_data, | |
+ tbl_size))) { | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
ret = -EFAULT; | |
goto argc_config_exit; | |
} | |
- if (copy_to_user(config->g_data, | |
- &mdss_pp_res->gc_lut_g[disp_num][0], tbl_size)) { | |
+ if ((tbl_size != local_cfg.num_g_stages * | |
+ sizeof(struct mdp_ar_gc_lut_data)) || | |
+ (copy_to_user(config->g_data, local_cfg.g_data, | |
+ tbl_size))) { | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
ret = -EFAULT; | |
goto argc_config_exit; | |
} | |
- if (copy_to_user(config->b_data, | |
- &mdss_pp_res->gc_lut_b[disp_num][0], tbl_size)) { | |
+ if ((tbl_size != local_cfg.num_b_stages * | |
+ sizeof(struct mdp_ar_gc_lut_data)) || | |
+ (copy_to_user(config->b_data, local_cfg.b_data, | |
+ tbl_size))) { | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
ret = -EFAULT; | |
goto argc_config_exit; | |
@@ -2948,22 +3242,24 @@ static int pp_histogram_enable(struct pp_hist_col_info *hist_info, | |
mutex_lock(&hist_info->hist_mutex); | |
/* check if it is idle */ | |
+ spin_lock_irqsave(&hist_info->hist_lock, flag); | |
if (hist_info->col_en) { | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
pr_info("%s Hist collection has already been enabled %d", | |
__func__, (u32) ctl_base); | |
ret = -EINVAL; | |
goto exit; | |
} | |
- hist_info->frame_cnt = req->frame_cnt; | |
- init_completion(&hist_info->comp); | |
- hist_info->hist_cnt_read = 0; | |
- hist_info->hist_cnt_sent = 0; | |
- hist_info->hist_cnt_time = 0; | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
hist_info->read_request = 0; | |
hist_info->col_state = HIST_RESET; | |
hist_info->col_en = true; | |
spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
+ hist_info->frame_cnt = req->frame_cnt; | |
+ INIT_COMPLETION(hist_info->comp); | |
+ INIT_COMPLETION(hist_info->first_kick); | |
+ hist_info->hist_cnt_read = 0; | |
+ hist_info->hist_cnt_sent = 0; | |
+ hist_info->hist_cnt_time = 0; | |
mdss_mdp_hist_intr_req(&mdata->hist_intr, 3 << shift_bit, true); | |
writel_relaxed(req->frame_cnt, ctl_base + 8); | |
/* Kick out reset start */ | |
@@ -3074,17 +3370,19 @@ static int pp_histogram_disable(struct pp_hist_col_info *hist_info, | |
struct mdss_data_type *mdata = mdss_mdp_get_mdata(); | |
mutex_lock(&hist_info->hist_mutex); | |
+ spin_lock_irqsave(&hist_info->hist_lock, flag); | |
if (hist_info->col_en == false) { | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
pr_debug("Histogram already disabled (%d)", (u32) ctl_base); | |
ret = -EINVAL; | |
goto exit; | |
} | |
- complete_all(&hist_info->comp); | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
hist_info->col_en = false; | |
hist_info->col_state = HIST_UNKNOWN; | |
spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
mdss_mdp_hist_intr_req(&mdata->hist_intr, done_bit, false); | |
+ complete_all(&hist_info->comp); | |
+ complete_all(&hist_info->first_kick); | |
writel_relaxed(BIT(1), ctl_base);/* cancel */ | |
ret = 0; | |
exit: | |
@@ -3317,7 +3615,7 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, | |
struct pp_hist_col_info *hist_info, | |
char __iomem *ctl_base, u32 expect_sum) | |
{ | |
- int wait_ret, ret = 0; | |
+ int kick_ret, wait_ret, ret = 0; | |
u32 timeout, sum; | |
char __iomem *v_base; | |
unsigned long flag; | |
@@ -3325,12 +3623,13 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, | |
struct mdss_mdp_pipe *pipe; | |
mutex_lock(&hist_info->hist_mutex); | |
+ spin_lock_irqsave(&hist_info->hist_lock, flag); | |
if ((hist_info->col_en == 0) || | |
(hist_info->col_state == HIST_UNKNOWN)) { | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
ret = -EINVAL; | |
goto hist_collect_exit; | |
} | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
/* wait for hist done if cache has no data */ | |
if (hist_info->col_state != HIST_READY) { | |
spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
@@ -3342,13 +3641,27 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, | |
pipe = container_of(res, struct mdss_mdp_pipe, pp_res); | |
pipe->params_changed++; | |
} | |
- wait_ret = wait_for_completion_killable_timeout( | |
+ kick_ret = wait_for_completion_killable_timeout( | |
+ &(hist_info->first_kick), timeout / | |
+ HIST_KICKOFF_WAIT_FRACTION); | |
+ if (kick_ret != 0) | |
+ wait_ret = wait_for_completion_killable_timeout( | |
&(hist_info->comp), timeout); | |
mutex_lock(&hist_info->hist_mutex); | |
- if (wait_ret == 0) { | |
+ spin_lock_irqsave(&hist_info->hist_lock, flag); | |
+ if (kick_ret == 0) { | |
+ ret = -ENODATA; | |
+ pr_debug("histogram kickoff not done yet"); | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
+ goto hist_collect_exit; | |
+ } else if (kick_ret < 0) { | |
+ ret = -EINTR; | |
+ pr_debug("histogram first kickoff interrupted"); | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
+ goto hist_collect_exit; | |
+ } else if (wait_ret == 0) { | |
ret = -ETIMEDOUT; | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
pr_debug("bin collection timedout, state %d", | |
hist_info->col_state); | |
/* | |
@@ -3363,37 +3676,36 @@ static int pp_hist_collect(struct mdp_histogram_data *hist, | |
*/ | |
hist_info->hist_cnt_time++; | |
hist_info->col_state = HIST_READY; | |
- spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
} else if (wait_ret < 0) { | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
ret = -EINTR; | |
pr_debug("%s: bin collection interrupted", | |
__func__); | |
goto hist_collect_exit; | |
} | |
- if (hist_info->col_state != HIST_READY) { | |
+ if (hist_info->col_state != HIST_READY && | |
+ hist_info->col_state != HIST_UNKNOWN) { | |
ret = -ENODATA; | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
hist_info->col_state = HIST_READY; | |
- spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
pr_debug("%s: state is not ready: %d", | |
__func__, hist_info->col_state); | |
} | |
- } else { | |
- spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
} | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
if (hist_info->col_state == HIST_READY) { | |
+ hist_info->col_state = HIST_IDLE; | |
spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
v_base = ctl_base + 0x1C; | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); | |
sum = pp_hist_read(v_base, hist_info); | |
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); | |
- spin_lock_irqsave(&hist_info->hist_lock, flag); | |
- if (expect_sum && sum != expect_sum) | |
+ if (expect_sum && sum != expect_sum) { | |
+ pr_debug("hist error: bin sum incorrect! (%d/%d)\n", | |
+ sum, expect_sum); | |
ret = -ENODATA; | |
- hist_info->col_state = HIST_IDLE; | |
+ } | |
+ } else { | |
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
} | |
- spin_unlock_irqrestore(&hist_info->hist_lock, flag); | |
hist_collect_exit: | |
mutex_unlock(&hist_info->hist_mutex); | |
return ret; | |
@@ -3457,6 +3769,9 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) | |
temp_ret = ret; | |
ret = pp_hist_collect(hist, hists[i], ctl_base, | |
exp_sum); | |
+ if (ret) | |
+ pr_debug("hist error: dspp[%d] collect %d\n", | |
+ dspp_num, ret); | |
} | |
for (i = 0; i < hist_cnt; i++) { | |
/* reset read requests and re-intialize completions */ | |
@@ -3554,6 +3869,10 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist) | |
temp_ret = ret; | |
ret = pp_hist_collect(hist, hist_info, ctl_base, | |
exp_sum); | |
+ if (ret) | |
+ pr_debug("hist error: pipe[%d] collect: %d\n", | |
+ pipe->num, ret); | |
+ | |
mdss_mdp_pipe_unmap(pipe); | |
} | |
for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) { | |
@@ -3669,8 +3988,7 @@ void mdss_mdp_hist_intr_done(u32 isr) | |
hist_info = &pipe->pp_res.hist; | |
} | |
/* Histogram Done Interrupt */ | |
- if (hist_info && (isr_blk & 0x1) && | |
- (hist_info->col_en)) { | |
+ if (hist_info && (isr_blk & 0x1) && (hist_info->col_en)) { | |
spin_lock(&hist_info->hist_lock); | |
hist_info->col_state = HIST_READY; | |
spin_unlock(&hist_info->hist_lock); | |
@@ -3680,8 +3998,7 @@ void mdss_mdp_hist_intr_done(u32 isr) | |
} | |
} | |
/* Histogram Reset Done Interrupt */ | |
- if ((isr_blk & 0x2) && | |
- (hist_info->col_en)) { | |
+ if (hist_info && (isr_blk & 0x2) && (hist_info->col_en)) { | |
spin_lock(&hist_info->hist_lock); | |
hist_info->col_state = HIST_IDLE; | |
spin_unlock(&hist_info->hist_lock); | |
@@ -3772,6 +4089,11 @@ static int mdss_ad_init_checks(struct msm_fb_data_type *mfd) | |
return -ENODEV; | |
} | |
+ if (ad_mfd->panel_info->type == DTV_PANEL) { | |
+ pr_debug("AD not supported on external display\n"); | |
+ return ret; | |
+ } | |
+ | |
mixer_num = mdss_mdp_get_ctl_mixers(ad_mfd->index, mixer_id); | |
if (!mixer_num) { | |
pr_debug("no mixers connected, %d", mixer_num); | |
@@ -3811,34 +4133,42 @@ static int mdss_mdp_get_ad(struct msm_fb_data_type *mfd, | |
return ret; | |
} | |
-static int pp_update_ad_input(struct msm_fb_data_type *mfd) | |
+/* must call this function from within ad->lock */ | |
+static int pp_ad_invalidate_input(struct msm_fb_data_type *mfd) | |
{ | |
int ret; | |
struct mdss_ad_info *ad; | |
- struct mdss_ad_input input; | |
struct mdss_mdp_ctl *ctl; | |
- if (!mfd) | |
+ if (!mfd) { | |
+ pr_err("Invalid mfd\n"); | |
return -EINVAL; | |
+ } | |
ctl = mfd_to_ctl(mfd); | |
- if (!ctl) | |
+ if (!ctl) { | |
+ pr_err("Invalid ctl\n"); | |
return -EINVAL; | |
+ } | |
ret = mdss_mdp_get_ad(mfd, &ad); | |
- if (ret) | |
- return ret; | |
- if (!ad || ad->cfg.mode == MDSS_AD_MODE_AUTO_BL) | |
+ if (ret || !ad) { | |
+ pr_err("Fail to get ad: ret = %d, ad = 0x%p\n", ret, ad); | |
+ return -EINVAL; | |
+ } | |
+ pr_debug("AD backlight level changed (%d), trigger update to AD\n", | |
+ mfd->ad_bl_level); | |
+ if (ad->cfg.mode == MDSS_AD_MODE_AUTO_BL) { | |
+ pr_err("AD auto backlight no longer supported.\n"); | |
return -EINVAL; | |
+ } | |
- pr_debug("backlight level changed (%d), trigger update to AD", | |
- mfd->bl_level); | |
- input.mode = ad->cfg.mode; | |
- if (MDSS_AD_MODE_DATA_MATCH(ad->cfg.mode, MDSS_AD_INPUT_AMBIENT)) | |
- input.in.amb_light = ad->ad_data; | |
- else | |
- input.in.strength = ad->ad_data; | |
- /* call to ad_input will trigger backlight read */ | |
- return mdss_mdp_ad_input(mfd, &input, 0); | |
+ if (ad->state & PP_AD_STATE_RUN) { | |
+ ad->calc_itr = ad->cfg.stab_itr; | |
+ ad->sts |= PP_AD_STS_DIRTY_VSYNC; | |
+ ad->sts |= PP_AD_STS_DIRTY_DATA; | |
+ } | |
+ | |
+ return 0; | |
} | |
int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, | |
@@ -3846,8 +4176,8 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, | |
{ | |
struct mdss_ad_info *ad; | |
struct msm_fb_data_type *bl_mfd; | |
- int lin_ret = -1, inv_ret = -1, ret = 0; | |
- u32 ratio_temp, shift = 0, last_ops; | |
+ int lin_ret = -1, inv_ret = -1, att_ret = -1, ret = 0; | |
+ u32 last_ops; | |
ret = mdss_mdp_get_ad(mfd, &ad); | |
if (ret) | |
@@ -3880,12 +4210,23 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, | |
sizeof(uint32_t)); | |
if (lin_ret || inv_ret) | |
ret = -ENOMEM; | |
- ratio_temp = mfd->panel_info->bl_max / AD_BL_LIN_LEN; | |
- while (ratio_temp > 0) { | |
- ratio_temp = ratio_temp >> 1; | |
- shift++; | |
- } | |
- ad->bl_bright_shift = shift; | |
+ } else { | |
+ ret = -EINVAL; | |
+ } | |
+ if (ret) { | |
+ ad->state &= ~PP_AD_STATE_BL_LIN; | |
+ goto ad_config_exit; | |
+ } else | |
+ ad->state |= PP_AD_STATE_BL_LIN; | |
+ | |
+ if ((init_cfg->params.init.bl_att_len == AD_BL_ATT_LUT_LEN) && | |
+ (init_cfg->params.init.bl_att_lut)) { | |
+ att_ret = copy_from_user(&ad->bl_att_lut, | |
+ init_cfg->params.init.bl_att_lut, | |
+ init_cfg->params.init.bl_att_len * | |
+ sizeof(uint32_t)); | |
+ if (att_ret) | |
+ ret = -ENOMEM; | |
} else { | |
ret = -EINVAL; | |
} | |
@@ -3899,11 +4240,7 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd, | |
} else if (init_cfg->ops & MDP_PP_AD_CFG) { | |
memcpy(&ad->cfg, &init_cfg->params.cfg, | |
sizeof(struct mdss_ad_cfg)); | |
- /* | |
- * TODO: specify panel independent range of input from cfg, | |
- * scale input backlight_scale to panel bl_max's range | |
- */ | |
- ad->cfg.backlight_scale = bl_mfd->panel_info->bl_max; | |
+ ad->cfg.backlight_scale = MDSS_MDP_AD_BL_SCALE; | |
ad->sts |= PP_AD_STS_DIRTY_CFG; | |
} | |
@@ -3972,7 +4309,7 @@ int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, | |
goto error; | |
} | |
ad->ad_data_mode = MDSS_AD_INPUT_AMBIENT; | |
- pr_debug("ambient = %d", input->in.amb_light); | |
+ pr_debug("ambient = %d\n", input->in.amb_light); | |
ad->ad_data = input->in.amb_light; | |
ad->calc_itr = ad->cfg.stab_itr; | |
ad->sts |= PP_AD_STS_DIRTY_VSYNC; | |
@@ -3991,7 +4328,7 @@ int mdss_mdp_ad_input(struct msm_fb_data_type *mfd, | |
goto error; | |
} | |
ad->ad_data_mode = MDSS_AD_INPUT_STRENGTH; | |
- pr_debug("strength = %d", input->in.strength); | |
+ pr_debug("strength = %d\n", input->in.strength); | |
ad->ad_data = input->in.strength; | |
ad->calc_itr = ad->cfg.stab_itr; | |
ad->sts |= PP_AD_STS_DIRTY_VSYNC; | |
@@ -4026,7 +4363,7 @@ error: | |
if (!ret) { | |
if (wait) { | |
mutex_lock(&ad->lock); | |
- init_completion(&ad->comp); | |
+ INIT_COMPLETION(ad->comp); | |
mutex_unlock(&ad->lock); | |
} | |
if (wait) { | |
@@ -4165,10 +4502,7 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad, | |
frame_end = 0xFFFF; | |
procs_start = 0x0; | |
procs_end = 0xFFFF; | |
- if (split_mode) | |
- tile_ctrl = 0x0; | |
- else | |
- tile_ctrl = 0x1; | |
+ tile_ctrl = 0x0; | |
} | |
@@ -4310,21 +4644,18 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) | |
*/ | |
ad->sts &= ~PP_AD_STS_DIRTY_DATA; | |
ad->state |= PP_AD_STATE_DATA; | |
- mutex_lock(&bl_mfd->bl_lock); | |
- bl = bl_mfd->bl_level; | |
pr_debug("dirty data, last_bl = %d ", ad->last_bl); | |
+ bl = bl_mfd->ad_bl_level; | |
+ | |
if ((ad->cfg.mode == MDSS_AD_MODE_AUTO_STR) && | |
(ad->last_bl != bl)) { | |
ad->last_bl = bl; | |
ad->calc_itr = ad->cfg.stab_itr; | |
ad->sts |= PP_AD_STS_DIRTY_VSYNC; | |
- if (ad->state & PP_AD_STATE_BL_LIN) { | |
- bl = ad->bl_lin[bl >> ad->bl_bright_shift]; | |
- bl = bl << ad->bl_bright_shift; | |
- } | |
- ad->bl_data = bl; | |
+ linear_map(bl, &ad->bl_data, | |
+ ad->bl_mfd->panel_info->bl_max, | |
+ MDSS_MDP_AD_BL_SCALE); | |
} | |
- mutex_unlock(&bl_mfd->bl_lock); | |
ad->reg_sts |= PP_AD_STS_DIRTY_DATA; | |
} | |
@@ -4368,13 +4699,9 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) | |
bypass = 0; | |
ad->reg_sts |= PP_AD_STS_DIRTY_ENABLE; | |
ad->state |= PP_AD_STATE_RUN; | |
- mutex_lock(&bl_mfd->bl_lock); | |
if (bl_mfd != mfd) | |
bl_mfd->ext_ad_ctrl = mfd->index; | |
- bl_mfd->mdp.update_ad_input = pp_update_ad_input; | |
bl_mfd->ext_bl_ctrl = ad->cfg.bl_ctrl_mode; | |
- mutex_unlock(&bl_mfd->bl_lock); | |
- | |
} else { | |
if (ad->state & PP_AD_STATE_RUN) { | |
ad->reg_sts = PP_AD_STS_DIRTY_ENABLE; | |
@@ -4385,7 +4712,6 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) | |
ad->state &= !PP_AD_STATE_CFG; | |
ad->state &= !PP_AD_STATE_DATA; | |
ad->state &= !PP_AD_STATE_BL_LIN; | |
- ad->bl_bright_shift = 0; | |
ad->ad_data = 0; | |
ad->ad_data_mode = 0; | |
ad->last_bl = 0; | |
@@ -4395,13 +4721,12 @@ static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd) | |
AD_BL_LIN_LEN); | |
memset(&ad->bl_lin_inv, 0, sizeof(uint32_t) * | |
AD_BL_LIN_LEN); | |
+ memset(&ad->bl_att_lut, 0, sizeof(uint32_t) * | |
+ AD_BL_ATT_LUT_LEN); | |
memset(&ad->init, 0, sizeof(struct mdss_ad_init)); | |
memset(&ad->cfg, 0, sizeof(struct mdss_ad_cfg)); | |
- mutex_lock(&bl_mfd->bl_lock); | |
- bl_mfd->mdp.update_ad_input = NULL; | |
bl_mfd->ext_bl_ctrl = 0; | |
bl_mfd->ext_ad_ctrl = -1; | |
- mutex_unlock(&bl_mfd->bl_lock); | |
} | |
ad->state &= ~PP_AD_STATE_RUN; | |
} | |
@@ -4443,7 +4768,7 @@ static void pp_ad_calc_worker(struct work_struct *work) | |
struct msm_fb_data_type *mfd, *bl_mfd; | |
struct mdss_data_type *mdata; | |
char __iomem *base; | |
- u32 bl, calc_done = 0; | |
+ u32 calc_done = 0; | |
ad = container_of(work, struct mdss_ad_info, calc_work); | |
mutex_lock(&ad->lock); | |
@@ -4456,7 +4781,7 @@ static void pp_ad_calc_worker(struct work_struct *work) | |
ctl = mfd_to_ctl(ad->mfd); | |
mdata = mfd_to_mdata(ad->mfd); | |
- if (!mdata || ad->calc_hw_num > mdata->nad_cfgs) { | |
+ if (!mdata || ad->calc_hw_num >= mdata->nad_cfgs) { | |
mutex_unlock(&ad->lock); | |
return; | |
} | |
@@ -4485,22 +4810,8 @@ static void pp_ad_calc_worker(struct work_struct *work) | |
if (calc_done) { | |
ad->last_str = 0xFF & readl_relaxed(base + | |
MDSS_MDP_REG_AD_STR_OUT); | |
- if (MDSS_AD_RUNNING_AUTO_BL(ad)) { | |
- bl = 0xFFFF & readl_relaxed(base + | |
- MDSS_MDP_REG_AD_BL_OUT); | |
- if (ad->state & PP_AD_STATE_BL_LIN) { | |
- bl = bl >> ad->bl_bright_shift; | |
- bl = min_t(u32, bl, (AD_BL_LIN_LEN-1)); | |
- bl = ad->bl_lin_inv[bl]; | |
- bl = bl << ad->bl_bright_shift; | |
- } | |
- pr_debug("calc bl = %d", bl); | |
- ad->last_str |= bl << 16; | |
- mutex_lock(&ad->bl_mfd->bl_lock); | |
- if (ad->bl_mfd->bl_level) | |
- mdss_fb_set_backlight(ad->bl_mfd, bl); | |
- mutex_unlock(&ad->bl_mfd->bl_lock); | |
- } | |
+ if (MDSS_AD_RUNNING_AUTO_BL(ad)) | |
+ pr_err("AD auto backlight no longer supported.\n"); | |
pr_debug("calc_str = %d, calc_itr %d", | |
ad->last_str & 0xFF, | |
ad->calc_itr); | |
@@ -4516,9 +4827,9 @@ static void pp_ad_calc_worker(struct work_struct *work) | |
ctl->remove_vsync_handler(ctl, &ad->handle); | |
} | |
mutex_unlock(&ad->lock); | |
- mutex_lock(&mfd->lock); | |
+ mutex_lock(&ctl->lock); | |
ctl->flush_bits |= BIT(13 + ad->num); | |
- mutex_unlock(&mfd->lock); | |
+ mutex_unlock(&ctl->lock); | |
/* Trigger update notify to wake up those waiting for display updates */ | |
mdss_fb_update_notify_update(bl_mfd); | |
@@ -4539,6 +4850,111 @@ static void pp_ad_cfg_lut(char __iomem *addr, u32 *data) | |
addr + ((PP_AD_LUT_LEN - 1) * 2)); | |
} | |
+/* must call this function from within ad->lock */ | |
+static int pp_ad_attenuate_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out) | |
+{ | |
+ u32 shift = 0, ratio_temp = 0; | |
+ u32 n, lut_interval, bl_att; | |
+ | |
+ if (bl < 0) { | |
+ pr_err("Invalid backlight input\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ pr_debug("bl_in = %d\n", bl); | |
+ /* map panel backlight range to AD backlight range */ | |
+ linear_map(bl, &bl, ad->bl_mfd->panel_info->bl_max, | |
+ MDSS_MDP_AD_BL_SCALE); | |
+ | |
+ pr_debug("Before attenuation = %d\n", bl); | |
+ ratio_temp = MDSS_MDP_AD_BL_SCALE / (AD_BL_ATT_LUT_LEN - 1); | |
+ while (ratio_temp > 0) { | |
+ ratio_temp = ratio_temp >> 1; | |
+ shift++; | |
+ } | |
+ n = bl >> shift; | |
+ if (n >= (AD_BL_ATT_LUT_LEN - 1)) { | |
+ pr_err("Invalid index for BL attenuation: %d.\n", n); | |
+ return -EINVAL; | |
+ } | |
+ lut_interval = (MDSS_MDP_AD_BL_SCALE + 1) / (AD_BL_ATT_LUT_LEN - 1); | |
+ bl_att = ad->bl_att_lut[n] + (bl - lut_interval * n) * | |
+ (ad->bl_att_lut[n + 1] - ad->bl_att_lut[n]) / | |
+ lut_interval; | |
+ pr_debug("n = %d, bl_att = %d\n", n, bl_att); | |
+ if (ad->init.alpha_base) | |
+ *bl_out = (ad->init.alpha * bl_att + | |
+ (ad->init.alpha_base - ad->init.alpha) * bl) / | |
+ ad->init.alpha_base; | |
+ else | |
+ *bl_out = bl; | |
+ | |
+ pr_debug("After attenuation = %d\n", *bl_out); | |
+ /* map AD backlight range back to panel backlight range */ | |
+ linear_map(*bl_out, bl_out, MDSS_MDP_AD_BL_SCALE, | |
+ ad->bl_mfd->panel_info->bl_max); | |
+ | |
+ pr_debug("bl_out = %d\n", *bl_out); | |
+ return 0; | |
+} | |
+ | |
+/* must call this function from within ad->lock */ | |
+static int pp_ad_linearize_bl(struct mdss_ad_info *ad, u32 bl, u32 *bl_out, | |
+ int inv) | |
+{ | |
+ | |
+ u32 n; | |
+ int ret = -EINVAL; | |
+ | |
+ if (bl < 0 || bl > ad->bl_mfd->panel_info->bl_max) { | |
+ pr_err("Invalid backlight input: bl = %d, bl_max = %d\n", bl, | |
+ ad->bl_mfd->panel_info->bl_max); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ pr_debug("bl_in = %d, inv = %d\n", bl, inv); | |
+ | |
+ /* map panel backlight range to AD backlight range */ | |
+ linear_map(bl, &bl, ad->bl_mfd->panel_info->bl_max, | |
+ MDSS_MDP_AD_BL_SCALE); | |
+ | |
+ pr_debug("Before linearization = %d\n", bl); | |
+ n = bl * (AD_BL_LIN_LEN - 1) / MDSS_MDP_AD_BL_SCALE; | |
+ pr_debug("n = %d\n", n); | |
+ if (n > (AD_BL_LIN_LEN - 1)) { | |
+ pr_err("Invalid index for BL linearization: %d.\n", n); | |
+ return ret; | |
+ } else if (n == (AD_BL_LIN_LEN - 1)) { | |
+ if (inv == MDP_PP_AD_BL_LINEAR_INV) | |
+ *bl_out = ad->bl_lin_inv[n]; | |
+ else if (inv == MDP_PP_AD_BL_LINEAR) | |
+ *bl_out = ad->bl_lin[n]; | |
+ } else { | |
+ /* linear piece-wise interpolation */ | |
+ if (inv == MDP_PP_AD_BL_LINEAR_INV) { | |
+ *bl_out = bl * (AD_BL_LIN_LEN - 1) * | |
+ (ad->bl_lin_inv[n + 1] - ad->bl_lin_inv[n]) / | |
+ MDSS_MDP_AD_BL_SCALE - n * | |
+ (ad->bl_lin_inv[n + 1] - ad->bl_lin_inv[n]) + | |
+ ad->bl_lin_inv[n]; | |
+ } else if (inv == MDP_PP_AD_BL_LINEAR) { | |
+ *bl_out = bl * (AD_BL_LIN_LEN - 1) * | |
+ (ad->bl_lin[n + 1] - ad->bl_lin[n]) / | |
+ MDSS_MDP_AD_BL_SCALE - | |
+ n * (ad->bl_lin[n + 1] - ad->bl_lin[n]) + | |
+ ad->bl_lin[n]; | |
+ } | |
+ } | |
+ pr_debug("After linearization = %d\n", *bl_out); | |
+ | |
+ /* map AD backlight range back to panel backlight range */ | |
+ linear_map(*bl_out, bl_out, MDSS_MDP_AD_BL_SCALE, | |
+ ad->bl_mfd->panel_info->bl_max); | |
+ | |
+ pr_debug("bl_out = %d\n", *bl_out); | |
+ return 0; | |
+} | |
+ | |
int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets) | |
{ | |
u32 i; | |
@@ -4574,6 +4990,7 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets) | |
mdata->ad_cfgs[i].last_str = 0xFFFFFFFF; | |
mdata->ad_cfgs[i].last_bl = 0; | |
mutex_init(&mdata->ad_cfgs[i].lock); | |
+ init_completion(&mdata->ad_cfgs[i].comp); | |
mdata->ad_cfgs[i].handle.vsync_handler = pp_ad_vsync_handler; | |
mdata->ad_cfgs[i].handle.cmd_post_flush = true; | |
INIT_WORK(&mdata->ad_cfgs[i].calc_work, pp_ad_calc_worker); | |
@@ -4601,7 +5018,8 @@ static int is_valid_calib_ctrl_addr(char __iomem *ptr) | |
break; | |
} | |
- for (stage = 0; stage < mdss_res->nmixers_intf; stage++) | |
+ for (stage = 0; stage < (mdss_res->nmixers_intf + | |
+ mdss_res->nmixers_wb); stage++) | |
if (ptr == base + MDSS_MDP_REG_CTL_LAYER(stage)) { | |
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; | |
goto End; | |
@@ -4692,7 +5110,10 @@ static int is_valid_calib_vig_addr(char __iomem *ptr) | |
} else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) { | |
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; | |
break; | |
- } else if ((ptr == base + MDSS_MDP_REG_VIG_QSEED2_SHARP)) { | |
+ /* QSEED2 range */ | |
+ } else if ((ptr >= base + MDSS_MDP_REG_VIG_QSEED2_SHARP) && | |
+ (ptr <= base + MDSS_MDP_REG_VIG_QSEED2_SHARP + | |
+ MDSS_MDP_VIG_QSEED2_SHARP_SIZE)) { | |
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE; | |
break; | |
/* PA range */ | |
@@ -4777,7 +5198,8 @@ static int is_valid_calib_mixer_addr(char __iomem *ptr) | |
int stage = 0; | |
struct mdss_mdp_mixer *mixer; | |
- for (counter = 0; counter < mdss_res->nmixers_intf; counter++) { | |
+ for (counter = 0; counter < (mdss_res->nmixers_intf + | |
+ mdss_res->nmixers_wb); counter++) { | |
mixer = mdss_res->mixer_intf + counter; | |
base = mixer->base; | |
@@ -4828,6 +5250,7 @@ static int is_valid_calib_addr(void *addr, u32 operation) | |
if ((unsigned int)addr % 4) { | |
ret = 0; | |
} else if (ptr == (mdss_res->mdp_base + MDSS_MDP_REG_HW_VERSION) || | |
+ ptr == (mdss_res->mdp_base + MDSS_REG_HW_VERSION) || | |
ptr == (mdss_res->mdp_base + MDSS_MDP_REG_DISP_INTF_SEL)) { | |
ret = MDP_PP_OPS_READ; | |
/* IGC DSPP range */ | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.c b/drivers/video/msm/mdss/mdss_mdp_rotator.c | |
old mode 100644 | |
new mode 100755 | |
index 442903b..41f7f94 | |
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.c | |
@@ -23,6 +23,7 @@ | |
#include "mdss_mdp.h" | |
#include "mdss_mdp_rotator.h" | |
#include "mdss_fb.h" | |
+#include "mdss_debug.h" | |
#define MAX_ROTATOR_SESSIONS 8 | |
@@ -227,7 +228,7 @@ static int __mdss_mdp_rotator_to_pipe(struct mdss_mdp_rotator_session *rot, | |
ret = mdss_mdp_smp_reserve(pipe); | |
if (ret) { | |
- pr_err("unable to mdss_mdp_smp_reserve rot data\n"); | |
+ pr_debug("unable to mdss_mdp_smp_reserve rot data\n"); | |
return ret; | |
} | |
@@ -282,8 +283,9 @@ static int mdss_mdp_rotator_queue_sub(struct mdss_mdp_rotator_session *rot, | |
pr_err("unable to queue rot data\n"); | |
goto error; | |
} | |
- | |
+ ATRACE_BEGIN("rotator_kickoff"); | |
ret = mdss_mdp_rotator_kickoff(rot_ctl, rot, dst_data); | |
+ ATRACE_END("rotator_kickoff"); | |
return ret; | |
error: | |
@@ -474,6 +476,12 @@ int mdss_mdp_rotator_setup(struct msm_fb_data_type *mfd, | |
goto rot_err; | |
} | |
+ if (work_pending(&rot->commit_work)) { | |
+ mutex_unlock(&rotator_lock); | |
+ flush_work(&rot->commit_work); | |
+ mutex_lock(&rotator_lock); | |
+ } | |
+ | |
if (rot->format != fmt->format) | |
format_changed = true; | |
@@ -617,14 +625,6 @@ int mdss_mdp_rotator_setup(struct msm_fb_data_type *mfd, | |
if (rot && (req->id == MSMFB_NEW_REQUEST)) | |
mdss_mdp_rotator_finish(rot); | |
} | |
- /* | |
- * overwrite the src format for rotator to dst format | |
- * for use by the user. On subsequent set calls, the | |
- * user is expected to proivde the original src format | |
- */ | |
- req->src.format = mdss_mdp_get_rotator_dst_format(req->src.format, | |
- req->flags & MDP_ROT_90); | |
- | |
mutex_unlock(&rotator_lock); | |
return ret; | |
} | |
@@ -647,6 +647,12 @@ static int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot) | |
rot_pipe = rot->pipe; | |
if (rot_pipe) { | |
+ if (work_pending(&rot->commit_work)) { | |
+ mutex_unlock(&rotator_lock); | |
+ cancel_work_sync(&rot->commit_work); | |
+ mutex_lock(&rotator_lock); | |
+ } | |
+ | |
mdss_mdp_rotator_busy_wait(rot); | |
list_del(&rot->head); | |
} | |
@@ -662,7 +668,7 @@ static int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot) | |
if (rot_pipe) { | |
struct mdss_mdp_mixer *mixer = rot_pipe->mixer; | |
- mdss_mdp_pipe_unmap(rot_pipe); | |
+ mdss_mdp_pipe_destroy(rot_pipe); | |
tmp = mdss_mdp_ctl_mixer_switch(mixer->ctl, | |
MDSS_MDP_WB_CTL_TYPE_BLOCK); | |
if (!tmp) | |
@@ -710,7 +716,6 @@ int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, | |
struct msmfb_overlay_data *req) | |
{ | |
struct mdss_mdp_rotator_session *rot; | |
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
int ret; | |
u32 flgs; | |
@@ -730,9 +735,6 @@ int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, | |
goto dst_buf_fail; | |
} | |
- if (!mfd->panel_info->cont_splash_enabled) | |
- mdss_iommu_attach(mdp5_data->mdata); | |
- | |
mdss_mdp_overlay_free_buf(&rot->src_buf); | |
ret = mdss_mdp_overlay_get_buf(mfd, &rot->src_buf, &req->data, 1, flgs); | |
if (ret) { | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_rotator.h b/drivers/video/msm/mdss/mdss_mdp_rotator.h | |
index b059a37..7229995 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_rotator.h | |
+++ b/drivers/video/msm/mdss/mdss_mdp_rotator.h | |
@@ -30,8 +30,8 @@ struct mdss_mdp_rotator_session { | |
u16 img_width; | |
u16 img_height; | |
- struct mdss_rect src_rect; | |
- struct mdss_rect dst; | |
+ struct mdss_mdp_img_rect src_rect; | |
+ struct mdss_mdp_img_rect dst; | |
u32 bwc_mode; | |
struct mdss_mdp_pipe *pipe; | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c | |
index f04c18a..01745fd 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_util.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c | |
@@ -243,9 +243,9 @@ struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format) | |
return NULL; | |
} | |
-void mdss_mdp_intersect_rect(struct mdss_rect *res_rect, | |
- const struct mdss_rect *dst_rect, | |
- const struct mdss_rect *sci_rect) | |
+void mdss_mdp_intersect_rect(struct mdss_mdp_img_rect *res_rect, | |
+ const struct mdss_mdp_img_rect *dst_rect, | |
+ const struct mdss_mdp_img_rect *sci_rect) | |
{ | |
int l = max(dst_rect->x, sci_rect->x); | |
int t = max(dst_rect->y, sci_rect->y); | |
@@ -253,16 +253,16 @@ void mdss_mdp_intersect_rect(struct mdss_rect *res_rect, | |
int b = min((dst_rect->y + dst_rect->h), (sci_rect->y + sci_rect->h)); | |
if (r < l || b < t) | |
- *res_rect = (struct mdss_rect){0, 0, 0, 0}; | |
+ *res_rect = (struct mdss_mdp_img_rect){0, 0, 0, 0}; | |
else | |
- *res_rect = (struct mdss_rect){l, t, (r-l), (b-t)}; | |
+ *res_rect = (struct mdss_mdp_img_rect){l, t, (r-l), (b-t)}; | |
} | |
-void mdss_mdp_crop_rect(struct mdss_rect *src_rect, | |
- struct mdss_rect *dst_rect, | |
- const struct mdss_rect *sci_rect) | |
+void mdss_mdp_crop_rect(struct mdss_mdp_img_rect *src_rect, | |
+ struct mdss_mdp_img_rect *dst_rect, | |
+ const struct mdss_mdp_img_rect *sci_rect) | |
{ | |
- struct mdss_rect res; | |
+ struct mdss_mdp_img_rect res; | |
mdss_mdp_intersect_rect(&res, dst_rect, sci_rect); | |
if (res.w && res.h) { | |
@@ -272,7 +272,7 @@ void mdss_mdp_crop_rect(struct mdss_rect *src_rect, | |
src_rect->w = res.w; | |
src_rect->h = res.h; | |
} | |
- *dst_rect = (struct mdss_rect) | |
+ *dst_rect = (struct mdss_mdp_img_rect) | |
{(res.x - sci_rect->x), (res.y - sci_rect->y), | |
res.w, res.h}; | |
} | |
@@ -498,24 +498,28 @@ int mdss_mdp_put_img(struct mdss_mdp_img_data *data) | |
data->srcp_file = NULL; | |
} else if (!IS_ERR_OR_NULL(data->srcp_ihdl)) { | |
pr_debug("ion hdl=%p buf=0x%x\n", data->srcp_ihdl, data->addr); | |
- | |
- if (is_mdss_iommu_attached()) { | |
- int domain; | |
- if (data->flags & MDP_SECURE_OVERLAY_SESSION) | |
- domain = MDSS_IOMMU_DOMAIN_SECURE; | |
- else | |
- domain = MDSS_IOMMU_DOMAIN_UNSECURE; | |
- ion_unmap_iommu(iclient, data->srcp_ihdl, | |
+ if (!iclient) { | |
+ pr_err("invalid ion client\n"); | |
+ return -ENOMEM; | |
+ } else { | |
+ if (is_mdss_iommu_attached()) { | |
+ int domain; | |
+ if (data->flags & MDP_SECURE_OVERLAY_SESSION) | |
+ domain = MDSS_IOMMU_DOMAIN_SECURE; | |
+ else | |
+ domain = MDSS_IOMMU_DOMAIN_UNSECURE; | |
+ ion_unmap_iommu(iclient, data->srcp_ihdl, | |
mdss_get_iommu_domain(domain), 0); | |
- if (domain == MDSS_IOMMU_DOMAIN_SECURE) { | |
- msm_ion_unsecure_buffer(iclient, | |
- data->srcp_ihdl); | |
+ if (domain == MDSS_IOMMU_DOMAIN_SECURE) { | |
+ msm_ion_unsecure_buffer(iclient, | |
+ data->srcp_ihdl); | |
+ } | |
} | |
+ ion_free(iclient, data->srcp_ihdl); | |
+ data->srcp_ihdl = NULL; | |
} | |
- ion_free(iclient, data->srcp_ihdl); | |
- data->srcp_ihdl = NULL; | |
} else { | |
return -ENOMEM; | |
} | |
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c | |
index 761b85a..a87b0ab 100644 | |
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c | |
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c | |
@@ -61,11 +61,14 @@ struct mdss_mdp_wb_data { | |
struct msmfb_data buf_info; | |
struct mdss_mdp_data buf_data; | |
int state; | |
+ bool user_alloc; | |
}; | |
static DEFINE_MUTEX(mdss_mdp_wb_buf_lock); | |
static struct mdss_mdp_wb mdss_mdp_wb_info; | |
+static void mdss_mdp_wb_free_node(struct mdss_mdp_wb_data *node); | |
+ | |
#ifdef DEBUG_WRITEBACK | |
/* for debugging: writeback output buffer to allocated memory */ | |
static inline | |
@@ -285,6 +288,7 @@ static int mdss_mdp_wb_terminate(struct msm_fb_data_type *mfd) | |
struct mdss_mdp_wb_data *node, *temp; | |
list_for_each_entry_safe(node, temp, &wb->register_queue, | |
registered_entry) { | |
+ mdss_mdp_wb_free_node(node); | |
list_del(&node->registered_entry); | |
kfree(node); | |
} | |
@@ -339,12 +343,12 @@ static int mdss_mdp_wb_stop(struct msm_fb_data_type *mfd) | |
static int mdss_mdp_wb_register_node(struct mdss_mdp_wb *wb, | |
struct mdss_mdp_wb_data *node) | |
{ | |
- node->state = REGISTERED; | |
- list_add_tail(&node->registered_entry, &wb->register_queue); | |
if (!node) { | |
pr_err("Invalid wb node\n"); | |
return -EINVAL; | |
} | |
+ node->state = REGISTERED; | |
+ list_add_tail(&node->registered_entry, &wb->register_queue); | |
return 0; | |
} | |
@@ -401,21 +405,59 @@ static struct mdss_mdp_wb_data *get_user_node(struct msm_fb_data_type *mfd, | |
struct mdss_mdp_img_data *buf; | |
int ret; | |
+ if (!list_empty(&wb->register_queue)) { | |
+ struct ion_client *iclient = mdss_get_ionclient(); | |
+ struct ion_handle *ihdl; | |
+ | |
+ if (!iclient) { | |
+ pr_err("iclient is NULL\n"); | |
+ return NULL; | |
+ } | |
+ | |
+ ihdl = ion_import_dma_buf(iclient, data->memory_id); | |
+ if (IS_ERR_OR_NULL(ihdl)) { | |
+ pr_err("unable to import fd %d\n", data->memory_id); | |
+ return NULL; | |
+ } | |
+ /* only interested in ptr address, so we can free handle */ | |
+ ion_free(iclient, ihdl); | |
+ | |
+ list_for_each_entry(node, &wb->register_queue, registered_entry) | |
+ if ((node->buf_data.p[0].srcp_ihdl == ihdl) && | |
+ (node->buf_info.offset == data->offset)) { | |
+ pr_debug("found fd=%d hdl=%p off=%x addr=%x\n", | |
+ data->memory_id, ihdl, | |
+ data->offset, | |
+ node->buf_data.p[0].addr); | |
+ return node; | |
+ } | |
+ } | |
+ | |
node = kzalloc(sizeof(struct mdss_mdp_wb_data), GFP_KERNEL); | |
if (node == NULL) { | |
pr_err("out of memory\n"); | |
return NULL; | |
} | |
+ node->user_alloc = true; | |
node->buf_data.num_planes = 1; | |
buf = &node->buf_data.p[0]; | |
if (wb->is_secure) | |
buf->flags |= MDP_SECURE_OVERLAY_SESSION; | |
+ | |
+ ret = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(ret)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ goto register_fail; | |
+ } | |
ret = mdss_mdp_get_img(data, buf); | |
if (IS_ERR_VALUE(ret)) { | |
pr_err("error getting buffer info\n"); | |
+ mdss_iommu_ctrl(0); | |
goto register_fail; | |
} | |
+ mdss_iommu_ctrl(0); | |
+ | |
memcpy(&node->buf_info, data, sizeof(*data)); | |
ret = mdss_mdp_wb_register_node(wb, node); | |
@@ -434,12 +476,28 @@ register_fail: | |
return NULL; | |
} | |
+static void mdss_mdp_wb_free_node(struct mdss_mdp_wb_data *node) | |
+{ | |
+ struct mdss_mdp_img_data *buf; | |
+ | |
+ if (node->user_alloc) { | |
+ buf = &node->buf_data.p[0]; | |
+ pr_debug("free user mem_id=%d ihdl=%p, offset=%u addr=0x%x\n", | |
+ node->buf_info.memory_id, | |
+ buf->srcp_ihdl, | |
+ node->buf_info.offset, | |
+ buf->addr); | |
+ | |
+ mdss_mdp_put_img(&node->buf_data.p[0]); | |
+ node->user_alloc = false; | |
+ } | |
+} | |
+ | |
static int mdss_mdp_wb_queue(struct msm_fb_data_type *mfd, | |
struct msmfb_data *data, int local) | |
{ | |
struct mdss_mdp_wb *wb = mfd_to_wb(mfd); | |
struct mdss_mdp_wb_data *node = NULL; | |
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); | |
int ret = 0; | |
if (!wb) { | |
@@ -449,9 +507,6 @@ static int mdss_mdp_wb_queue(struct msm_fb_data_type *mfd, | |
pr_debug("fb%d queue\n", wb->fb_ndx); | |
- if (!mfd->panel_info->cont_splash_enabled) | |
- mdss_iommu_attach(mdp5_data->mdata); | |
- | |
mutex_lock(&wb->lock); | |
if (local) | |
node = get_local_node(wb, data); | |
@@ -510,6 +565,7 @@ static int mdss_mdp_wb_dequeue(struct msm_fb_data_type *mfd, | |
{ | |
struct mdss_mdp_wb *wb = mfd_to_wb(mfd); | |
struct mdss_mdp_wb_data *node = NULL; | |
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
int ret; | |
if (!wb) { | |
@@ -517,6 +573,11 @@ static int mdss_mdp_wb_dequeue(struct msm_fb_data_type *mfd, | |
return -ENODEV; | |
} | |
+ if (!ctl) { | |
+ pr_err("unable to dequeue, ctl is not initialized\n"); | |
+ return -ENODEV; | |
+ } | |
+ | |
ret = wait_event_interruptible(wb->wait_q, is_buffer_ready(wb)); | |
if (ret) { | |
pr_err("failed to get dequeued buffer\n"); | |
@@ -526,6 +587,7 @@ static int mdss_mdp_wb_dequeue(struct msm_fb_data_type *mfd, | |
mutex_lock(&wb->lock); | |
if (wb->state == WB_STOPING) { | |
pr_debug("wfd stopped\n"); | |
+ mdss_mdp_display_wait4comp(ctl); | |
wb->state = WB_STOP; | |
ret = -ENOBUFS; | |
} else if (!list_empty(&wb->busy_queue)) { | |
@@ -639,77 +701,30 @@ int mdss_mdp_wb_set_mirr_hint(struct msm_fb_data_type *mfd, int hint) | |
int mdss_mdp_wb_get_format(struct msm_fb_data_type *mfd, | |
struct mdp_mixer_cfg *mixer_cfg) | |
{ | |
- int dst_format; | |
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
if (!ctl) { | |
pr_err("No panel data!\n"); | |
return -EINVAL; | |
+ } else { | |
+ mixer_cfg->writeback_format = ctl->dst_format; | |
} | |
- switch (ctl->dst_format) { | |
- case MDP_RGB_888: | |
- dst_format = WB_FORMAT_RGB_888; | |
- break; | |
- case MDP_RGB_565: | |
- dst_format = WB_FORMAT_RGB_565; | |
- break; | |
- case MDP_XRGB_8888: | |
- dst_format = WB_FORMAT_xRGB_8888; | |
- break; | |
- case MDP_ARGB_8888: | |
- dst_format = WB_FORMAT_ARGB_8888; | |
- break; | |
- case MDP_BGRA_8888: | |
- dst_format = WB_FORMAT_BGRA_8888; | |
- break; | |
- case MDP_BGRX_8888: | |
- dst_format = WB_FORMAT_BGRX_8888; | |
- break; | |
- case MDP_Y_CBCR_H2V2_VENUS: | |
- dst_format = WB_FORMAT_NV12; | |
- break; | |
- default: | |
- return -EINVAL; | |
- } | |
- mixer_cfg->writeback_format = dst_format; | |
return 0; | |
} | |
-int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, int dst_format) | |
+int mdss_mdp_wb_set_format(struct msm_fb_data_type *mfd, u32 dst_format) | |
{ | |
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); | |
if (!ctl) { | |
pr_err("No panel data!\n"); | |
return -EINVAL; | |
- } | |
- | |
- switch (dst_format) { | |
- case WB_FORMAT_RGB_888: | |
- ctl->dst_format = MDP_RGB_888; | |
- break; | |
- case WB_FORMAT_RGB_565: | |
- ctl->dst_format = MDP_RGB_565; | |
- break; | |
- case WB_FORMAT_xRGB_8888: | |
- ctl->dst_format = MDP_XRGB_8888; | |
- break; | |
- case WB_FORMAT_ARGB_8888: | |
- ctl->dst_format = MDP_ARGB_8888; | |
- break; | |
- case WB_FORMAT_BGRA_8888: | |
- ctl->dst_format = MDP_BGRA_8888; | |
- break; | |
- case WB_FORMAT_BGRX_8888: | |
- ctl->dst_format = MDP_BGRX_8888; | |
- break; | |
- case WB_FORMAT_NV12: | |
- ctl->dst_format = MDP_Y_CBCR_H2V2_VENUS; | |
- break; | |
- default: | |
- pr_err("wfd format not supported\n"); | |
+ } else if (dst_format >= MDP_IMGTYPE_LIMIT2) { | |
+ pr_err("Invalid dst format=%u\n", dst_format); | |
return -EINVAL; | |
+ } else { | |
+ ctl->dst_format = dst_format; | |
} | |
pr_debug("wfd format %d\n", ctl->dst_format); | |
@@ -751,7 +766,13 @@ int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, | |
} | |
break; | |
case MSMFB_WRITEBACK_TERMINATE: | |
+ ret = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(ret)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ return ret; | |
+ } | |
ret = mdss_mdp_wb_terminate(mfd); | |
+ mdss_iommu_ctrl(0); | |
break; | |
case MSMFB_WRITEBACK_SET_MIRRORING_HINT: | |
if (!copy_from_user(&hint, arg, sizeof(hint))) { | |
@@ -862,3 +883,28 @@ int msm_fb_writeback_set_secure(struct fb_info *info, int enable) | |
return mdss_mdp_wb_set_secure(mfd, enable); | |
} | |
EXPORT_SYMBOL(msm_fb_writeback_set_secure); | |
+ | |
+/** | |
+ * msm_fb_writeback_iommu_ref() - Add/Remove vote on MDSS IOMMU being attached. | |
+ * @enable - true adds vote on MDSS IOMMU, false removes the vote. | |
+ * | |
+ * Call to vote on MDSS IOMMU being enabled. To ensure buffers are properly | |
+ * mapped to IOMMU context bank. | |
+ */ | |
+int msm_fb_writeback_iommu_ref(struct fb_info *info, int enable) | |
+{ | |
+ int ret; | |
+ | |
+ if (enable) { | |
+ ret = mdss_iommu_ctrl(1); | |
+ if (IS_ERR_VALUE(ret)) { | |
+ pr_err("IOMMU attach failed\n"); | |
+ return ret; | |
+ } | |
+ } else { | |
+ mdss_iommu_ctrl(0); | |
+ } | |
+ | |
+ return 0; | |
+} | |
+EXPORT_SYMBOL(msm_fb_writeback_iommu_ref); | |
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h | |
index 44205ff..8713db4 100644 | |
--- a/drivers/video/msm/mdss/mdss_panel.h | |
+++ b/drivers/video/msm/mdss/mdss_panel.h | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2008-2014, 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 | |
@@ -71,13 +71,6 @@ enum { | |
MODE_GPIO_LOW, | |
}; | |
-struct mdss_rect { | |
- u16 x; | |
- u16 y; | |
- u16 w; | |
- u16 h; | |
-}; | |
- | |
#define MDSS_MAX_PANEL_LEN 256 | |
#define MDSS_INTF_MAX_NAME_LEN 5 | |
struct mdss_panel_intf { | |
@@ -134,6 +127,15 @@ struct mdss_panel_recovery { | |
- 1 clock enable | |
* @MDSS_EVENT_ENABLE_PARTIAL_UPDATE: Event to update ROI of the panel. | |
* @MDSS_EVENT_DSI_CMDLIST_KOFF: acquire dsi_mdp_busy lock before kickoff. | |
+ * @MDSS_EVENT_DSI_ULPS_CTRL: Event to configure Ultra Lower Power Saving | |
+ * mode for the DSI data and clock lanes. The | |
+ * event arguments can have one of these values: | |
+ * - 0: Disable ULPS mode | |
+ * - 1: Enable ULPS mode | |
+ * @MDSS_EVENT_DSI_DYNAMIC_SWITCH: Event to update the dsi driver structures | |
+ * based on the dsi mode passed as argument. | |
+ * - 0: update to video mode | |
+ * - 1: update to command mode | |
*/ | |
enum mdss_intf_events { | |
MDSS_EVENT_RESET = 1, | |
@@ -152,6 +154,9 @@ enum mdss_intf_events { | |
MDSS_EVENT_PANEL_CLK_CTRL, | |
MDSS_EVENT_DSI_CMDLIST_KOFF, | |
MDSS_EVENT_ENABLE_PARTIAL_UPDATE, | |
+ MDSS_EVENT_DSI_ULPS_CTRL, | |
+ MDSS_EVENT_REGISTER_RECOVERY_HANDLER, | |
+ MDSS_EVENT_DSI_DYNAMIC_SWITCH, | |
}; | |
struct lcd_panel_info { | |
@@ -209,6 +214,7 @@ struct mipi_panel_info { | |
char hbp_power_stop; | |
char hsa_power_stop; | |
char eof_bllp_power_stop; | |
+ char last_line_interleave_en; | |
char bllp_power_stop; | |
char traffic_mode; | |
char frame_rate; | |
@@ -221,6 +227,9 @@ struct mipi_panel_info { | |
char stream; /* 0 or 1 */ | |
char mdp_trigger; | |
char dma_trigger; | |
+ /*Dynamic Switch Support*/ | |
+ bool dynamic_switch_enabled; | |
+ u32 pixel_packing; | |
u32 dsi_pclk_rate; | |
/* The packet-size should not bet changed */ | |
char no_max_pkt_size; | |
@@ -241,6 +250,7 @@ struct edp_panel_info { | |
enum dynamic_fps_update { | |
DFPS_SUSPEND_RESUME_MODE, | |
DFPS_IMMEDIATE_CLK_UPDATE_MODE, | |
+ DFPS_IMMEDIATE_PORCH_UPDATE_MODE, | |
}; | |
enum lvds_mode { | |
@@ -274,6 +284,17 @@ struct fbc_panel_info { | |
u32 lossy_mode_idx; | |
}; | |
+struct mdss_mdp_pp_tear_check { | |
+ u32 tear_check_en; | |
+ u32 sync_cfg_height; | |
+ u32 vsync_init_val; | |
+ u32 sync_threshold_start; | |
+ u32 sync_threshold_continue; | |
+ u32 start_pos; | |
+ u32 rd_ptr_irq; | |
+ u32 refx100; | |
+}; | |
+ | |
struct mdss_panel_info { | |
u32 xres; | |
u32 yres; | |
@@ -306,8 +327,18 @@ struct mdss_panel_info { | |
int pwm_period; | |
u32 mode_gpio_state; | |
bool dynamic_fps; | |
+ bool ulps_feature_enabled; | |
+ bool esd_check_enabled; | |
char dfps_update; | |
int new_fps; | |
+ int panel_max_fps; | |
+ int panel_max_vtotal; | |
+ u32 xstart_pix_align; | |
+ u32 width_pix_align; | |
+ u32 ystart_pix_align; | |
+ u32 height_pix_align; | |
+ u32 min_width; | |
+ u32 min_height; | |
u32 cont_splash_enabled; | |
u32 partial_update_enabled; | |
@@ -315,9 +346,12 @@ struct mdss_panel_info { | |
u32 panel_power_on; | |
uint32_t panel_dead; | |
+ bool dynamic_switch_pending; | |
+ bool is_lpm_mode; | |
+ | |
+ struct mdss_mdp_pp_tear_check te; | |
struct lcd_panel_info lcdc; | |
- struct lcd_panel_info lcdc_tune; | |
struct fbc_panel_info fbc; | |
struct mipi_panel_info mipi; | |
struct lvds_panel_info lvds; | |
@@ -388,36 +422,6 @@ static inline u32 mdss_panel_get_framerate(struct mdss_panel_info *panel_info) | |
} | |
/* | |
- * mdss_rect_cmp() - compares two rects | |
- * @rect1 - rect value to compare | |
- * @rect2 - rect value to compare | |
- * | |
- * Returns 1 if the rects are same, 0 otherwise. | |
- */ | |
-static inline int mdss_rect_cmp(struct mdss_rect *rect1, | |
- struct mdss_rect *rect2) { | |
- return (rect1->x == rect2->x && rect1->y == rect2->y && | |
- rect1->w == rect2->w && rect1->h == rect2->h); | |
-} | |
- | |
-/* | |
- * mdss_panel_get_vtotal_lcd() - return panel vertical height | |
- * @pinfo: Pointer to panel info containing all panel information | |
- * @lcd: Pointer to lcdc panel info with timings | |
- * | |
- * Returns the total height of the panel including any blanking regions | |
- * which are not visible to user but used to calculate panel pixel clock. | |
- * The caller may specify an alternate set of lcd timings. | |
- */ | |
-static inline int mdss_panel_get_vtotal_lcd(struct mdss_panel_info *pinfo, | |
- struct lcd_panel_info *lcd) | |
-{ | |
- return pinfo->yres + lcd->v_back_porch + | |
- lcd->v_front_porch + | |
- lcd->v_pulse_width; | |
-} | |
- | |
-/* | |
* mdss_panel_get_vtotal() - return panel vertical height | |
* @pinfo: Pointer to panel info containing all panel information | |
* | |
@@ -426,7 +430,9 @@ static inline int mdss_panel_get_vtotal_lcd(struct mdss_panel_info *pinfo, | |
*/ | |
static inline int mdss_panel_get_vtotal(struct mdss_panel_info *pinfo) | |
{ | |
- return mdss_panel_get_vtotal_lcd(pinfo, &pinfo->lcdc); | |
+ return pinfo->yres + pinfo->lcdc.v_back_porch + | |
+ pinfo->lcdc.v_front_porch + | |
+ pinfo->lcdc.v_pulse_width; | |
} | |
/* | |
diff --git a/drivers/video/msm/mdss/mhl_sii8334.c b/drivers/video/msm/mdss/mhl_sii8334.c | |
index 0f84b2df..9f50f89 100644 | |
--- a/drivers/video/msm/mdss/mhl_sii8334.c | |
+++ b/drivers/video/msm/mdss/mhl_sii8334.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -194,6 +194,7 @@ static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, | |
static void mhl_init_reg_settings(struct mhl_tx_ctrl *mhl_ctrl, | |
bool mhl_disc_en); | |
static int mhl_gpio_config(struct mhl_tx_ctrl *mhl_ctrl, int on); | |
+static int mhl_vreg_config(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on); | |
int mhl_i2c_reg_read(struct i2c_client *client, | |
uint8_t slave_addr_index, uint8_t reg_offset) | |
@@ -385,15 +386,77 @@ static int mhl_sii_wait_for_rgnd(struct mhl_tx_ctrl *mhl_ctrl) | |
return 0; | |
} | |
+static int mhl_sii_config(struct mhl_tx_ctrl *mhl_ctrl, bool on) | |
+{ | |
+ int rc = 0; | |
+ struct i2c_client *client = NULL; | |
+ | |
+ if (!mhl_ctrl) { | |
+ pr_err("%s: ctrl is NULL\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ client = mhl_ctrl->i2c_handle; | |
+ | |
+ mutex_lock(&mhl_ctrl->sii_config_lock); | |
+ if (on && !mhl_ctrl->irq_req_done) { | |
+ rc = mhl_vreg_config(mhl_ctrl, 1); | |
+ if (rc) { | |
+ pr_err("%s: vreg init failed [%d]\n", | |
+ __func__, rc); | |
+ rc = -ENODEV; | |
+ goto vreg_config_error; | |
+ } | |
+ | |
+ rc = mhl_gpio_config(mhl_ctrl, 1); | |
+ if (rc) { | |
+ pr_err("%s: gpio init failed [%d]\n", | |
+ __func__, rc); | |
+ rc = -ENODEV; | |
+ goto vreg_config_error; | |
+ } | |
+ | |
+ rc = request_threaded_irq(mhl_ctrl->i2c_handle->irq, NULL, | |
+ &mhl_tx_isr, IRQF_TRIGGER_LOW | IRQF_ONESHOT, | |
+ client->dev.driver->name, mhl_ctrl); | |
+ if (rc) { | |
+ pr_err("%s: request_threaded_irq failed, status: %d\n", | |
+ __func__, rc); | |
+ rc = -ENODEV; | |
+ goto vreg_config_error; | |
+ } else { | |
+ mhl_ctrl->irq_req_done = true; | |
+ /* wait for i2c interrupt line to be activated */ | |
+ msleep(100); | |
+ } | |
+ } else if (!on && mhl_ctrl->irq_req_done) { | |
+ free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl); | |
+ mhl_gpio_config(mhl_ctrl, 0); | |
+ mhl_vreg_config(mhl_ctrl, 0); | |
+ mhl_ctrl->irq_req_done = false; | |
+ } | |
+ | |
+vreg_config_error: | |
+ mutex_unlock(&mhl_ctrl->sii_config_lock); | |
+ return rc; | |
+} | |
+ | |
+static void mhl_sii_disc_intr_work(struct work_struct *work) | |
+{ | |
+ struct mhl_tx_ctrl *mhl_ctrl = NULL; | |
+ | |
+ mhl_ctrl = container_of(work, struct mhl_tx_ctrl, mhl_intr_work); | |
+ | |
+ mhl_sii_config(mhl_ctrl, false); | |
+} | |
+ | |
/* USB_HANDSHAKING FUNCTIONS */ | |
static int mhl_sii_device_discovery(void *data, int id, | |
void (*usb_notify_cb)(void *, int), void *ctx) | |
{ | |
int rc; | |
struct mhl_tx_ctrl *mhl_ctrl = data; | |
- struct i2c_client *client = mhl_ctrl->i2c_handle; | |
unsigned long flags; | |
- int discovery_retry = 5; | |
if (id) { | |
/* When MHL cable is disconnected we get a sii8334 | |
@@ -413,22 +476,13 @@ static int mhl_sii_device_discovery(void *data, int id, | |
mhl_ctrl->notify_usb_online = usb_notify_cb; | |
mhl_ctrl->notify_ctx = ctx; | |
} | |
-again: | |
- if (!mhl_ctrl->irq_req_done) { | |
- rc = request_threaded_irq(mhl_ctrl->i2c_handle->irq, NULL, | |
- &mhl_tx_isr, IRQF_TRIGGER_LOW | IRQF_ONESHOT, | |
- client->dev.driver->name, mhl_ctrl); | |
- if (rc) { | |
- pr_debug("request_threaded_irq failed, status: %d\n", | |
- rc); | |
- return -EINVAL; | |
- } else { | |
- pr_debug("request_threaded_irq succeeded\n"); | |
- mhl_ctrl->irq_req_done = true; | |
- } | |
- /* wait for i2c interrupt line to be activated */ | |
- msleep(100); | |
+ flush_work(&mhl_ctrl->mhl_intr_work); | |
+ | |
+ rc = mhl_sii_config(mhl_ctrl, true); | |
+ if (rc) { | |
+ pr_err("%s: Failed to config vreg/gpio\n", __func__); | |
+ return rc; | |
} | |
if (!mhl_ctrl->disc_enabled) { | |
@@ -448,23 +502,9 @@ again: | |
if (mhl_sii_wait_for_rgnd(mhl_ctrl)) { | |
pr_err("%s: discovery timeout\n", __func__); | |
- free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl); | |
- mhl_gpio_config(mhl_ctrl, 0); | |
- mhl_ctrl->irq_req_done = false; | |
- | |
- msleep(100); | |
+ mhl_sii_config(mhl_ctrl, false); | |
- mhl_gpio_config(mhl_ctrl, 1); | |
- if (discovery_retry--) { | |
- pr_debug("%s: retrying discovery\n", __func__); | |
- goto again; | |
- } else { | |
- pr_err("%s: discovery failed, ret to USB\n", | |
- __func__); | |
- if (mhl_ctrl->notify_usb_online) | |
- mhl_ctrl->notify_usb_online( | |
- mhl_ctrl->notify_ctx, 0); | |
- } | |
+ return -EAGAIN; | |
} | |
} else { | |
if (mhl_ctrl->cur_state == POWER_STATE_D3) { | |
@@ -1059,13 +1099,8 @@ static int dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl) | |
mhl_msm_connection(mhl_ctrl); | |
} else if (status & BIT3) { | |
pr_debug("%s: uUSB-a type dev detct\n", __func__); | |
- | |
- /* Short RGND */ | |
- MHL_SII_REG_NAME_MOD(REG_DISC_STAT2, BIT0 | BIT1, 0x00); | |
- mhl_msm_disconnection(mhl_ctrl); | |
power_supply_changed(&mhl_ctrl->mhl_psy); | |
- if (mhl_ctrl->notify_usb_online) | |
- mhl_ctrl->notify_usb_online(mhl_ctrl->notify_ctx, 0); | |
+ mhl_drive_hpd(mhl_ctrl, HPD_DOWN); | |
return 0; | |
} | |
@@ -1081,6 +1116,9 @@ static int dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl) | |
power_supply_changed(&mhl_ctrl->mhl_psy); | |
if (mhl_ctrl->notify_usb_online) | |
mhl_ctrl->notify_usb_online(mhl_ctrl->notify_ctx, 0); | |
+ | |
+ queue_work(mhl_ctrl->mhl_workq, &mhl_ctrl->mhl_intr_work); | |
+ | |
return 0; | |
} | |
@@ -1495,6 +1533,34 @@ static int mhl_sii_reg_config(struct i2c_client *client, bool enable) | |
int rc = -EINVAL; | |
pr_debug("%s\n", __func__); | |
+ | |
+ if (!enable) { | |
+ if (reg_8941_vdda) { | |
+ regulator_disable(reg_8941_vdda); | |
+ regulator_put(reg_8941_vdda); | |
+ reg_8941_vdda = NULL; | |
+ } | |
+ | |
+ if (reg_8941_smps3a) { | |
+ regulator_disable(reg_8941_smps3a); | |
+ regulator_put(reg_8941_smps3a); | |
+ reg_8941_smps3a = NULL; | |
+ } | |
+ | |
+ if (reg_8941_l02) { | |
+ regulator_disable(reg_8941_l02); | |
+ regulator_put(reg_8941_l02); | |
+ reg_8941_l02 = NULL; | |
+ } | |
+ | |
+ if (reg_8941_l24) { | |
+ regulator_disable(reg_8941_l24); | |
+ regulator_put(reg_8941_l24); | |
+ reg_8941_l24 = NULL; | |
+ } | |
+ return 0; | |
+ } | |
+ | |
if (!reg_8941_l24) { | |
reg_8941_l24 = regulator_get(&client->dev, | |
"avcc_18"); | |
@@ -1736,26 +1802,6 @@ static int mhl_i2c_probe(struct i2c_client *client, | |
} | |
/* | |
- * Regulator init | |
- */ | |
- rc = mhl_vreg_config(mhl_ctrl, 1); | |
- if (rc) { | |
- pr_err("%s: vreg init failed [%d]\n", | |
- __func__, rc); | |
- goto failed_probe; | |
- } | |
- | |
- /* | |
- * GPIO init | |
- */ | |
- rc = mhl_gpio_config(mhl_ctrl, 1); | |
- if (rc) { | |
- pr_err("%s: gpio init failed [%d]\n", | |
- __func__, rc); | |
- goto failed_probe; | |
- } | |
- | |
- /* | |
* Other initializations | |
* such tx specific | |
*/ | |
@@ -1767,6 +1813,9 @@ static int mhl_i2c_probe(struct i2c_client *client, | |
spin_lock_init(&mhl_ctrl->lock); | |
mhl_ctrl->msc_send_workqueue = create_singlethread_workqueue | |
("mhl_msc_cmd_queue"); | |
+ mhl_ctrl->mhl_workq = create_singlethread_workqueue("mhl_workq"); | |
+ | |
+ INIT_WORK(&mhl_ctrl->mhl_intr_work, mhl_sii_disc_intr_work); | |
mhl_ctrl->input = input_allocate_device(); | |
if (mhl_ctrl->input) { | |
@@ -1819,6 +1868,7 @@ static int mhl_i2c_probe(struct i2c_client *client, | |
init_completion(&mhl_ctrl->rgnd_done); | |
+ mutex_init(&mhl_ctrl->sii_config_lock); | |
mhl_ctrl->mhl_psy.name = "ext-vbus"; | |
@@ -1894,9 +1944,7 @@ static int mhl_i2c_probe(struct i2c_client *client, | |
failed_probe_pwr: | |
power_supply_unregister(&mhl_ctrl->mhl_psy); | |
failed_probe: | |
- free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl); | |
- mhl_gpio_config(mhl_ctrl, 0); | |
- mhl_vreg_config(mhl_ctrl, 0); | |
+ mhl_sii_config(mhl_ctrl, false); | |
/* do not deep-free */ | |
if (mhl_info) | |
devm_kfree(&client->dev, mhl_info); | |
@@ -1923,9 +1971,10 @@ static int mhl_i2c_remove(struct i2c_client *client) | |
return -EINVAL; | |
} | |
- free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl); | |
- mhl_gpio_config(mhl_ctrl, 0); | |
- mhl_vreg_config(mhl_ctrl, 0); | |
+ mhl_sii_config(mhl_ctrl, false); | |
+ | |
+ destroy_workqueue(mhl_ctrl->mhl_workq); | |
+ | |
if (mhl_ctrl->mhl_info) | |
devm_kfree(&client->dev, mhl_ctrl->mhl_info); | |
if (mhl_ctrl->pdata) | |
@@ -1949,17 +1998,19 @@ static int mhl_i2c_suspend_sub(struct i2c_client *client) | |
pr_debug("%s\n", __func__); | |
- if (!mhl_ctrl) | |
+ if (!mhl_ctrl) { | |
+ pr_err("%s: invalid ctrl data\n", __func__); | |
return 0; | |
- | |
- free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl); | |
- mhl_ctrl->irq_req_done = false; | |
+ } | |
if (mhl_ctrl->mhl_mode) { | |
mhl_ctrl->mhl_mode = 0; | |
+ | |
power_supply_changed(&mhl_ctrl->mhl_psy); | |
if (mhl_ctrl->notify_usb_online) | |
mhl_ctrl->notify_usb_online(mhl_ctrl->notify_ctx, 0); | |
+ | |
+ mhl_sii_config(mhl_ctrl, false); | |
} | |
return 0; | |
diff --git a/drivers/video/msm/mdss/msm_mdss_io_8974.c b/drivers/video/msm/mdss/msm_mdss_io_8974.c | |
index 5d4610c..671fb50 100644 | |
--- a/drivers/video/msm/mdss/msm_mdss_io_8974.c | |
+++ b/drivers/video/msm/mdss/msm_mdss_io_8974.c | |
@@ -1,4 +1,4 @@ | |
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | |
+/* Copyright (c) 2012-2014, 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 | |
@@ -64,6 +64,17 @@ int mdss_dsi_clk_init(struct platform_device *pdev, | |
goto mdss_dsi_clk_err; | |
} | |
+ if ((ctrl_pdata->panel_data.panel_info.type == MIPI_CMD_PANEL) || | |
+ ctrl_pdata->panel_data.panel_info.mipi.dynamic_switch_enabled) { | |
+ ctrl_pdata->mmss_misc_ahb_clk = clk_get(dev, "core_mmss_clk"); | |
+ if (IS_ERR(ctrl_pdata->mmss_misc_ahb_clk)) { | |
+ rc = PTR_ERR(ctrl_pdata->mmss_misc_ahb_clk); | |
+ pr_err("%s: Unable to get mmss misc ahb clk. rc=%d\n", | |
+ __func__, rc); | |
+ goto mdss_dsi_clk_err; | |
+ } | |
+ } | |
+ | |
ctrl_pdata->byte_clk = clk_get(dev, "byte_clk"); | |
if (IS_ERR(ctrl_pdata->byte_clk)) { | |
rc = PTR_ERR(ctrl_pdata->byte_clk); | |
@@ -105,6 +116,8 @@ void mdss_dsi_clk_deinit(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
clk_put(ctrl_pdata->esc_clk); | |
if (ctrl_pdata->pixel_clk) | |
clk_put(ctrl_pdata->pixel_clk); | |
+ if (ctrl_pdata->mmss_misc_ahb_clk) | |
+ clk_put(ctrl_pdata->mmss_misc_ahb_clk); | |
if (ctrl_pdata->axi_clk) | |
clk_put(ctrl_pdata->axi_clk); | |
if (ctrl_pdata->ahb_clk) | |
@@ -204,7 +217,7 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, | |
} | |
/* find the mnd settings from mnd_table entry */ | |
- for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { | |
+ for (; mnd_entry < mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) { | |
if (((mnd_entry->lanes) == lanes) && | |
((mnd_entry->bpp) == bpp)) | |
break; | |
@@ -247,10 +260,12 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, | |
return 0; | |
} | |
-int mdss_dsi_enable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static int mdss_dsi_bus_clk_start(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
int rc = 0; | |
+ pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx); | |
+ | |
rc = clk_prepare_enable(ctrl_pdata->mdp_core_clk); | |
if (rc) { | |
pr_err("%s: failed to enable mdp_core_clock. rc=%d\n", | |
@@ -273,18 +288,32 @@ int mdss_dsi_enable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
goto error; | |
} | |
+ if (ctrl_pdata->mmss_misc_ahb_clk) { | |
+ rc = clk_prepare_enable(ctrl_pdata->mmss_misc_ahb_clk); | |
+ if (rc) { | |
+ pr_err("%s: failed to enable mmss misc ahb clk.rc=%d\n", | |
+ __func__, rc); | |
+ clk_disable_unprepare(ctrl_pdata->axi_clk); | |
+ clk_disable_unprepare(ctrl_pdata->ahb_clk); | |
+ clk_disable_unprepare(ctrl_pdata->mdp_core_clk); | |
+ goto error; | |
+ } | |
+ } | |
+ | |
error: | |
return rc; | |
} | |
-void mdss_dsi_disable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static void mdss_dsi_bus_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
+ if (ctrl_pdata->mmss_misc_ahb_clk) | |
+ clk_disable_unprepare(ctrl_pdata->mmss_misc_ahb_clk); | |
clk_disable_unprepare(ctrl_pdata->axi_clk); | |
clk_disable_unprepare(ctrl_pdata->ahb_clk); | |
clk_disable_unprepare(ctrl_pdata->mdp_core_clk); | |
} | |
-static int mdss_dsi_clk_prepare(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static int mdss_dsi_link_clk_prepare(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
int rc = 0; | |
@@ -316,7 +345,7 @@ esc_clk_err: | |
return rc; | |
} | |
-static void mdss_dsi_clk_unprepare(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static void mdss_dsi_link_clk_unprepare(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
if (!ctrl_pdata) { | |
pr_err("%s: Invalid input data\n", __func__); | |
@@ -328,7 +357,7 @@ static void mdss_dsi_clk_unprepare(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
clk_unprepare(ctrl_pdata->esc_clk); | |
} | |
-static int mdss_dsi_clk_set_rate(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static int mdss_dsi_link_clk_set_rate(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
u32 esc_clk_rate = 19200000; | |
int rc = 0; | |
@@ -369,7 +398,7 @@ error: | |
return rc; | |
} | |
-static int mdss_dsi_clk_enable(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static int mdss_dsi_link_clk_enable(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
int rc = 0; | |
@@ -378,10 +407,7 @@ static int mdss_dsi_clk_enable(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
return -EINVAL; | |
} | |
- if (ctrl_pdata->mdss_dsi_clk_on) { | |
- pr_info("%s: mdss_dsi_clks already ON\n", __func__); | |
- return 0; | |
- } | |
+ pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx); | |
rc = clk_enable(ctrl_pdata->esc_clk); | |
if (rc) { | |
@@ -401,8 +427,6 @@ static int mdss_dsi_clk_enable(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
goto pixel_clk_err; | |
} | |
- ctrl_pdata->mdss_dsi_clk_on = 1; | |
- | |
return rc; | |
pixel_clk_err: | |
@@ -413,80 +437,240 @@ esc_clk_err: | |
return rc; | |
} | |
-static void mdss_dsi_clk_disable(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
+static void mdss_dsi_link_clk_disable(struct mdss_dsi_ctrl_pdata *ctrl_pdata) | |
{ | |
if (!ctrl_pdata) { | |
pr_err("%s: Invalid input data\n", __func__); | |
return; | |
} | |
- if (ctrl_pdata->mdss_dsi_clk_on == 0) { | |
- pr_info("%s: mdss_dsi_clks already OFF\n", __func__); | |
- return; | |
- } | |
+ pr_debug("%s: ndx=%d\n", __func__, ctrl_pdata->ndx); | |
clk_disable(ctrl_pdata->esc_clk); | |
clk_disable(ctrl_pdata->pixel_clk); | |
clk_disable(ctrl_pdata->byte_clk); | |
+} | |
+ | |
+static int mdss_dsi_link_clk_start(struct mdss_dsi_ctrl_pdata *ctrl) | |
+{ | |
+ int rc = 0; | |
+ | |
+ rc = mdss_dsi_link_clk_set_rate(ctrl); | |
+ if (rc) { | |
+ pr_err("%s: failed to set clk rates. rc=%d\n", | |
+ __func__, rc); | |
+ goto error; | |
+ } | |
+ | |
+ rc = mdss_dsi_link_clk_prepare(ctrl); | |
+ if (rc) { | |
+ pr_err("%s: failed to prepare clks. rc=%d\n", | |
+ __func__, rc); | |
+ goto error; | |
+ } | |
+ | |
+ rc = mdss_dsi_link_clk_enable(ctrl); | |
+ if (rc) { | |
+ pr_err("%s: failed to enable clks. rc=%d\n", | |
+ __func__, rc); | |
+ mdss_dsi_link_clk_unprepare(ctrl); | |
+ goto error; | |
+ } | |
- ctrl_pdata->mdss_dsi_clk_on = 0; | |
+error: | |
+ return rc; | |
} | |
-int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable) | |
+static void mdss_dsi_link_clk_stop(struct mdss_dsi_ctrl_pdata *ctrl) | |
+{ | |
+ mdss_dsi_link_clk_disable(ctrl); | |
+ mdss_dsi_link_clk_unprepare(ctrl); | |
+} | |
+ | |
+static int __mdss_dsi_update_clk_cnt(u32 *clk_cnt, int enable) | |
+{ | |
+ int changed = 0; | |
+ | |
+ if (enable) { | |
+ if (*clk_cnt == 0) | |
+ changed++; | |
+ (*clk_cnt)++; | |
+ } else { | |
+ if (*clk_cnt != 0) { | |
+ (*clk_cnt)--; | |
+ if (*clk_cnt == 0) | |
+ changed++; | |
+ } else { | |
+ pr_debug("%s: clk cnt already zero\n", __func__); | |
+ } | |
+ } | |
+ | |
+ return changed; | |
+} | |
+ | |
+static int mdss_dsi_clk_ctrl_sub(struct mdss_dsi_ctrl_pdata *ctrl, | |
+ u8 clk_type, int enable) | |
{ | |
int rc = 0; | |
- mutex_lock(&ctrl->mutex); | |
+ if (!ctrl) { | |
+ pr_err("%s: Invalid arg\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ pr_debug("%s: ndx=%d clk_type=%08x enable=%d\n", __func__, | |
+ ctrl->ndx, clk_type, enable); | |
+ | |
if (enable) { | |
- if (ctrl->clk_cnt == 0) { | |
- rc = mdss_dsi_enable_bus_clocks(ctrl); | |
+ if (clk_type & DSI_BUS_CLKS) { | |
+ rc = mdss_dsi_bus_clk_start(ctrl); | |
if (rc) { | |
- pr_err("%s: failed to enable bus clks. rc=%d\n", | |
- __func__, rc); | |
+ pr_err("Failed to start bus clocks. rc=%d\n", | |
+ rc); | |
goto error; | |
} | |
- | |
- rc = mdss_dsi_clk_set_rate(ctrl); | |
+ } | |
+ if (clk_type & DSI_LINK_CLKS) { | |
+ rc = mdss_dsi_link_clk_start(ctrl); | |
if (rc) { | |
- pr_err("%s: failed to set clk rates. rc=%d\n", | |
- __func__, rc); | |
- mdss_dsi_disable_bus_clocks(ctrl); | |
+ pr_err("Failed to start link clocks. rc=%d\n", | |
+ rc); | |
+ if (clk_type & DSI_BUS_CLKS) | |
+ mdss_dsi_bus_clk_stop(ctrl); | |
goto error; | |
} | |
+ } | |
+ } else { | |
+ if (clk_type & DSI_LINK_CLKS) | |
+ mdss_dsi_link_clk_stop(ctrl); | |
+ if (clk_type & DSI_BUS_CLKS) | |
+ mdss_dsi_bus_clk_stop(ctrl); | |
+ } | |
- rc = mdss_dsi_clk_prepare(ctrl); | |
- if (rc) { | |
- pr_err("%s: failed to prepare clks. rc=%d\n", | |
- __func__, rc); | |
- mdss_dsi_disable_bus_clocks(ctrl); | |
- goto error; | |
- } | |
+error: | |
+ return rc; | |
+} | |
+ | |
+static DEFINE_MUTEX(dsi_clk_lock); /* per system */ | |
+ | |
+bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type) | |
+{ | |
+ bool bus_enabled = true; | |
+ bool link_enabled = true; | |
- rc = mdss_dsi_clk_enable(ctrl); | |
+ mutex_lock(&dsi_clk_lock); | |
+ if (clk_type & DSI_BUS_CLKS) | |
+ bus_enabled = ctrl->bus_clk_cnt ? true : false; | |
+ if (clk_type & DSI_LINK_CLKS) | |
+ link_enabled = ctrl->link_clk_cnt ? true : false; | |
+ mutex_unlock(&dsi_clk_lock); | |
+ | |
+ return bus_enabled && link_enabled; | |
+} | |
+ | |
+int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, | |
+ u8 clk_type, int enable) | |
+{ | |
+ int rc = 0; | |
+ int changed = 0, m_changed = 0; | |
+ struct mdss_dsi_ctrl_pdata *mctrl = NULL; | |
+ | |
+ if (!ctrl) { | |
+ pr_err("%s: Invalid arg\n", __func__); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ /* | |
+ * In broadcast mode, we need to enable clocks for the | |
+ * master controller as well when enabling clocks for the | |
+ * slave controller | |
+ */ | |
+ if (mdss_dsi_is_slave_ctrl(ctrl)) { | |
+ mctrl = mdss_dsi_get_master_ctrl(); | |
+ if (!mctrl) | |
+ pr_warn("%s: Unable to get master control\n", __func__); | |
+ } | |
+ | |
+ pr_debug("%s++: ndx=%d clk_type=%d bus_clk_cnt=%d link_clk_cnt=%d", | |
+ __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\n, 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) { | |
+ changed = __mdss_dsi_update_clk_cnt(&ctrl->bus_clk_cnt, | |
+ enable); | |
+ if (changed && mctrl) | |
+ m_changed = __mdss_dsi_update_clk_cnt( | |
+ &mctrl->bus_clk_cnt, enable); | |
+ } | |
+ | |
+ if (clk_type & DSI_LINK_CLKS) { | |
+ changed += __mdss_dsi_update_clk_cnt(&ctrl->link_clk_cnt, | |
+ enable); | |
+ if (changed && mctrl) | |
+ m_changed += __mdss_dsi_update_clk_cnt( | |
+ &mctrl->link_clk_cnt, enable); | |
+ } | |
+ | |
+ if (changed) { | |
+ if (enable && m_changed) { | |
+ rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable); | |
if (rc) { | |
- pr_err("%s: failed to enable clks. rc=%d\n", | |
- __func__, rc); | |
- mdss_dsi_clk_unprepare(ctrl); | |
- mdss_dsi_disable_bus_clocks(ctrl); | |
- goto error; | |
+ pr_err("Failed to start mctrl clocks. rc=%d\n", | |
+ rc); | |
+ goto error_mctrl_start; | |
} | |
} | |
- ctrl->clk_cnt++; | |
- } else { | |
- if (ctrl->clk_cnt) { | |
- ctrl->clk_cnt--; | |
- if (ctrl->clk_cnt == 0) { | |
- mdss_dsi_clk_disable(ctrl); | |
- mdss_dsi_clk_unprepare(ctrl); | |
- mdss_dsi_disable_bus_clocks(ctrl); | |
+ | |
+ rc = mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable); | |
+ if (rc) { | |
+ pr_err("Failed to %s ctrl clocks. rc=%d\n", | |
+ (enable ? "start" : "stop"), rc); | |
+ goto error_ctrl; | |
+ } | |
+ | |
+ if (!enable && m_changed) { | |
+ rc = mdss_dsi_clk_ctrl_sub(mctrl, clk_type, enable); | |
+ if (rc) { | |
+ pr_err("Failed to stop mctrl clocks. rc=%d\n", | |
+ rc); | |
+ goto error_mctrl_stop; | |
} | |
} | |
} | |
- pr_debug("%s: ctrl ndx=%d enabled=%d clk_cnt=%d\n", | |
- __func__, ctrl->ndx, enable, ctrl->clk_cnt); | |
+ goto no_error; | |
+ | |
+error_mctrl_stop: | |
+ mdss_dsi_clk_ctrl_sub(ctrl, clk_type, enable ? 0 : 1); | |
+error_ctrl: | |
+ if (enable && m_changed) | |
+ mdss_dsi_clk_ctrl_sub(mctrl, clk_type, 0); | |
+error_mctrl_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", | |
+ __func__, ctrl->ndx, clk_type, ctrl->bus_clk_cnt, | |
+ ctrl->link_clk_cnt, changed); | |
+ pr_debug("%s++: mctrl=%s m_bus_clk_cnt=%d m_link_clk_cnt=%d\n, m_changed=%d, enable=%d\n", | |
+ __func__, mctrl ? "yes" : "no", mctrl ? mctrl->bus_clk_cnt : -1, | |
+ mctrl ? mctrl->link_clk_cnt : -1, m_changed, enable); | |
-error: | |
- mutex_unlock(&ctrl->mutex); | |
return rc; | |
} | |
@@ -502,75 +686,54 @@ void mdss_dsi_phy_sw_reset(unsigned char *ctrl_base) | |
wmb(); | |
} | |
-void mdss_dsi_phy_enable(struct mdss_dsi_ctrl_pdata *ctrl, int on) | |
+void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl) | |
{ | |
- static struct mdss_dsi_ctrl_pdata *left_ctrl; | |
+ struct mdss_dsi_ctrl_pdata *ctrl0 = NULL; | |
if (ctrl == NULL) { | |
pr_err("%s: Invalid input data\n", __func__); | |
return; | |
} | |
- if (!left_ctrl | |
- && ctrl->shared_pdata.broadcast_enable) | |
- if ((ctrl->panel_data).panel_info.pdest | |
- == DISPLAY_1) | |
- left_ctrl = ctrl; | |
- | |
- if (on) { | |
- MIPI_OUTP(ctrl->ctrl_base + 0x03cc, 0x03); | |
- wmb(); | |
- usleep(100); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0220, 0x006); | |
- wmb(); | |
- usleep(100); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0268, 0x001); | |
- wmb(); | |
- usleep(100); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0268, 0x000); | |
- wmb(); | |
- usleep(100); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0220, 0x007); | |
- wmb(); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x03cc, 0x01); | |
- wmb(); | |
- usleep(100); | |
- | |
- /* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */ | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x07e); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x06e); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x06c); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x064); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x065); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x075); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x077); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x07f); | |
- wmb(); | |
- } else { | |
- if (left_ctrl && | |
- (ctrl->panel_data.panel_info.pdest | |
- == DISPLAY_1)) | |
- return; | |
+ /* | |
+ * 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 | |
+ * or not. | |
+ */ | |
+ if ((ctrl->ndx == DSI_CTRL_0) && | |
+ mdss_dsi_get_ctrl_by_index(DSI_CTRL_1)) { | |
+ pr_debug("%s: Dual dsi detected. skipping config for ctrl%d\n", | |
+ __func__, ctrl->ndx); | |
+ return; | |
+ } | |
- if (left_ctrl && | |
- (ctrl->panel_data.panel_info.pdest | |
- == DISPLAY_2)) { | |
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0220, 0x006); | |
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0470, 0x000); | |
- MIPI_OUTP(left_ctrl->ctrl_base + 0x0598, 0x000); | |
+ if (ctrl->ndx == DSI_CTRL_1) { | |
+ ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0); | |
+ if (ctrl0) { | |
+ MIPI_OUTP(ctrl0->phy_io.base + 0x0170, 0x000); | |
+ MIPI_OUTP(ctrl0->phy_io.base + 0x0298, 0x000); | |
+ } else { | |
+ pr_warn("%s: Unable to get control%d\n", | |
+ __func__, DSI_CTRL_0); | |
} | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0220, 0x006); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0470, 0x000); | |
- MIPI_OUTP(ctrl->ctrl_base + 0x0598, 0x000); | |
- wmb(); | |
} | |
+ | |
+ MIPI_OUTP(ctrl->phy_io.base + 0x0170, 0x000); | |
+ MIPI_OUTP(ctrl->phy_io.base + 0x0298, 0x000); | |
+ | |
+ /* | |
+ * Wait for the registers writes to complete in order to | |
+ * ensure that the phy is completely disabled | |
+ */ | |
+ wmb(); | |
} | |
void mdss_dsi_phy_init(struct mdss_panel_data *pdata) | |
{ | |
struct mdss_dsi_phy_ctrl *pd; | |
int i, off, ln, offset; | |
- struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; | |
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL, *temp_ctrl = NULL; | |
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, | |
panel_data); | |
@@ -578,67 +741,75 @@ void mdss_dsi_phy_init(struct mdss_panel_data *pdata) | |
pr_err("%s: Invalid input data\n", __func__); | |
return; | |
} | |
+ temp_ctrl = ctrl_pdata; | |
pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db); | |
/* Strength ctrl 0 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0484, pd->strength[0]); | |
- | |
- /* phy regulator ctrl settings. Both the DSI controller | |
- have one regulator */ | |
- if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1) | |
- off = 0x0580; | |
- else | |
- off = 0x0580 - 0x600; | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0184, pd->strength[0]); | |
+ | |
+ /* | |
+ * Phy regulator ctrl settings. | |
+ * In dual dsi configuration, the second controller also uses | |
+ * the regulators of the first controller, irrespective of whether | |
+ * broadcast mode is enabled or not. | |
+ */ | |
+ if (ctrl_pdata->ndx == DSI_CTRL_1) { | |
+ temp_ctrl = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0); | |
+ if (!temp_ctrl) { | |
+ pr_err("%s: Unable to get master ctrl\n", __func__); | |
+ return; | |
+ } | |
+ } | |
/* Regulator ctrl 0 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 0), 0x0); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x280, 0x0); | |
/* Regulator ctrl - CAL_PWR_CFG */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 6), pd->regulator[6]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x298, pd->regulator[6]); | |
/* Regulator ctrl - TEST */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 5), pd->regulator[5]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x294, pd->regulator[5]); | |
/* Regulator ctrl 3 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 3), pd->regulator[3]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x28c, pd->regulator[3]); | |
/* Regulator ctrl 2 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 2), pd->regulator[2]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x288, pd->regulator[2]); | |
/* Regulator ctrl 1 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 1), pd->regulator[1]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x284, pd->regulator[1]); | |
/* Regulator ctrl 0 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 0), pd->regulator[0]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x280, pd->regulator[0]); | |
/* Regulator ctrl 4 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off + (4 * 4), pd->regulator[4]); | |
+ MIPI_OUTP((temp_ctrl->phy_io.base) + 0x290, pd->regulator[4]); | |
/* LDO ctrl 0 */ | |
if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1) | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x4dc, 0x00); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00); | |
else | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x4dc, 0x00); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00); | |
- off = 0x0440; /* phy timing ctrl 0 - 11 */ | |
+ off = 0x0140; /* phy timing ctrl 0 - 11 */ | |
for (i = 0; i < 12; i++) { | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, pd->timing[i]); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->timing[i]); | |
wmb(); | |
off += 4; | |
} | |
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_1 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0474, 0x00); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0174, 0x00); | |
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0470, 0x5f); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f); | |
wmb(); | |
/* Strength ctrl 1 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0488, pd->strength[1]); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0188, pd->strength[1]); | |
wmb(); | |
/* 4 lanes + clk lane configuration */ | |
/* lane config n * (0 - 4) & DataPath setup */ | |
for (ln = 0; ln < 5; ln++) { | |
- off = 0x0300 + (ln * 0x40); | |
+ off = (ln * 0x40); | |
for (i = 0; i < 9; i++) { | |
offset = i + (ln * 9); | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, | |
pd->lanecfg[offset]); | |
wmb(); | |
off += 4; | |
@@ -646,19 +817,19 @@ void mdss_dsi_phy_init(struct mdss_panel_data *pdata) | |
} | |
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */ | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x0470, 0x5f); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f); | |
wmb(); | |
/* DSI_0_PHY_DSIPHY_GLBL_TEST_CTRL */ | |
if ((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1) | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04d4, 0x01); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x01); | |
else | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x04d4, 0x00); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x00); | |
wmb(); | |
- off = 0x04b4; /* phy BIST ctrl 0 - 5 */ | |
+ off = 0x01b4; /* phy BIST ctrl 0 - 5 */ | |
for (i = 0; i < 6; i++) { | |
- MIPI_OUTP((ctrl_pdata->ctrl_base) + off, pd->bistctrl[i]); | |
+ MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->bistctrl[i]); | |
wmb(); | |
off += 4; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment