Skip to content

Instantly share code, notes, and snippets.

@invisiblek
Created March 28, 2015 15:29
Show Gist options
  • Save invisiblek/f221d678a69574533a58 to your computer and use it in GitHub Desktop.
Save invisiblek/f221d678a69574533a58 to your computer and use it in GitHub Desktop.
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 43f94e8..4eb1033 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -9,17 +9,18 @@ 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
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_6g.c b/drivers/video/msm/mdss/dsi_status_6g.c
index 31c9022..2dcd1c8 100644
--- a/drivers/video/msm/mdss/dsi_status_6g.c
+++ b/drivers/video/msm/mdss/dsi_status_6g.c
@@ -38,6 +38,10 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
pstatus_data = container_of(to_delayed_work(work),
struct dsi_status_data, check_status);
+ if (!pstatus_data || !(pstatus_data->mfd)) {
+ pr_err("%s: mfd not available\n", __func__);
+ return;
+ }
pdata = dev_get_platdata(&pstatus_data->mfd->pdev->dev);
if (!pdata) {
@@ -56,6 +60,19 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
mdp5_data = mfd_to_mdp5_data(pstatus_data->mfd);
ctl = mfd_to_ctl(pstatus_data->mfd);
+ if (!ctl) {
+ pr_err("%s: Display is off\n", __func__);
+ return;
+ }
+
+ if (!ctl->power_on) {
+ schedule_delayed_work(&pstatus_data->check_status,
+ msecs_to_jiffies(interval));
+ pr_err("%s: ctl not powered on\n", __func__);
+ return;
+ }
+
+ mutex_lock(&ctrl_pdata->mutex);
if (ctl->shared_lock)
mutex_lock(ctl->shared_lock);
mutex_lock(&mdp5_data->ov_lock);
@@ -64,7 +81,8 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
mutex_unlock(&mdp5_data->ov_lock);
if (ctl->shared_lock)
mutex_unlock(ctl->shared_lock);
- pr_err("%s: DSI turning off, avoiding BTA status check\n",
+ mutex_unlock(&ctrl_pdata->mutex);
+ pr_err("%s: DSI turning off, avoiding panel status check\n",
__func__);
return;
}
@@ -91,6 +109,7 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
mutex_unlock(&mdp5_data->ov_lock);
if (ctl->shared_lock)
mutex_unlock(ctl->shared_lock);
+ mutex_unlock(&ctrl_pdata->mutex);
if ((pstatus_data->mfd->panel_power_on)) {
if (ret > 0) {
diff --git a/drivers/video/msm/mdss/dsi_status_v2.c b/drivers/video/msm/mdss/dsi_status_v2.c
index c8915e6..f0966cc 100644
--- a/drivers/video/msm/mdss/dsi_status_v2.c
+++ b/drivers/video/msm/mdss/dsi_status_v2.c
@@ -40,6 +40,11 @@ void mdp3_check_dsi_ctrl_status(struct work_struct *work,
pdsi_status = container_of(to_delayed_work(work),
struct dsi_status_data, check_status);
+ if (!pdsi_status || !(pdsi_status->mfd)) {
+ pr_err("%s: mfd 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__);
@@ -56,6 +61,11 @@ void mdp3_check_dsi_ctrl_status(struct work_struct *work,
}
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");
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 93474cd..d4d913f 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -190,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;
@@ -210,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;
@@ -280,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);
}
@@ -402,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;
}
@@ -424,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);
@@ -579,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)
@@ -634,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;
@@ -645,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;
}
@@ -675,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;
}
@@ -686,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;
}
@@ -698,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;
}
@@ -708,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;
}
@@ -1904,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");
@@ -1914,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();
@@ -1942,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);
@@ -1963,10 +2029,9 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata)
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;
}
@@ -1986,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;
}
@@ -2000,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();
}
}
@@ -2304,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;
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
index 9d6afe8..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;
@@ -184,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,
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index e947d5c1d..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,6 +160,7 @@ 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);
}
@@ -294,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;
}
@@ -327,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);
}
@@ -341,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;
@@ -363,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;
}
@@ -424,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;
}
@@ -475,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;
@@ -503,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;
@@ -528,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);
@@ -576,12 +593,6 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
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) {
@@ -604,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");
@@ -623,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;
@@ -652,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;
@@ -661,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");
@@ -686,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_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;
@@ -755,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);
@@ -790,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");
@@ -843,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);
@@ -890,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;
@@ -916,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;
@@ -924,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 {
@@ -992,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;
@@ -1000,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;
@@ -1010,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;
}
@@ -1052,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) {
@@ -1078,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);
@@ -1279,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");
@@ -1305,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;
}
@@ -1615,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;
@@ -1631,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;
}
@@ -1772,6 +1781,48 @@ int mdp3_wait_for_dma_done(struct mdp3_session_data *session)
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;
@@ -1794,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) {
@@ -1873,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 416b7c2..93356e2 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -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,7 @@ 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;
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 80e4e81..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,12 @@ 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 {
@@ -311,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);
@@ -320,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,
@@ -330,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,
@@ -338,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);
@@ -352,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 116b778..02540bb 100644
--- a/drivers/video/msm/mdss/mdss_dsi_status.c
+++ b/drivers/video/msm/mdss/mdss_dsi_status.c
@@ -32,7 +32,7 @@
#define STATUS_CHECK_INTERVAL_MS 5000
#define STATUS_CHECK_INTERVAL_MIN_MS 200
-#define DSI_STATUS_CHECK_DISABLE 1
+#define DSI_STATUS_CHECK_DISABLE 0
static uint32_t interval = STATUS_CHECK_INTERVAL_MS;
static uint32_t dsi_status_disable = DSI_STATUS_CHECK_DISABLE;
@@ -81,6 +81,7 @@ static int fb_event_callback(struct notifier_block *self,
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),
@@ -89,6 +90,14 @@ static int fb_event_callback(struct notifier_block *self,
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;
@@ -106,8 +115,14 @@ static int fb_event_callback(struct notifier_block *self,
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;
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 d72724d..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,12 +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;
};
@@ -180,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;
@@ -204,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)
@@ -244,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 e111156..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)
@@ -83,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",
@@ -155,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)
{
@@ -316,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,
@@ -327,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)
@@ -362,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);
@@ -370,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)
@@ -513,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;
@@ -613,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
@@ -620,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)
{
@@ -650,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);
}
}
@@ -686,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);
@@ -701,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);
}
@@ -786,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++) {
@@ -804,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)
@@ -819,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;
}
@@ -839,7 +904,6 @@ int mdss_iommu_dettach(struct mdss_data_type *mdata)
}
mdata->iommu_attached = false;
- mutex_unlock(&mdp_iommu_lock);
return 0;
}
@@ -962,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;
}
@@ -1009,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");
@@ -1052,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();
@@ -1060,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;
}
@@ -1088,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)
@@ -1099,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;
@@ -1219,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) {
@@ -1540,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");
@@ -1559,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)
{
@@ -1745,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:
@@ -2015,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);
@@ -2026,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,
@@ -2036,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);
@@ -2047,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;
}
@@ -2090,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)
{
@@ -2256,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;
@@ -2353,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;
@@ -2377,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");
@@ -2385,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 c317934..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);
@@ -646,10 +717,6 @@ int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id);
u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp);
void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval);
-#ifdef CONFIG_MACH_LGE
-int mdss_dsi_panel_invert(u32 enable);
-#endif
-
int mdss_panel_register_done(struct mdss_panel_data *pdata);
int mdss_mdp_limited_lut_igc_config(struct mdss_mdp_ctl *ctl);
int mdss_mdp_calib_config(struct mdp_calib_config_data *cfg, u32 *copyback);
@@ -663,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