summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev/msm
diff options
context:
space:
mode:
authorAdrian Salido-Moreno <adrianm@codeaurora.org>2016-04-06 09:29:37 -0700
committerBryan Huntsman <bryanh@codeaurora.org>2016-04-12 15:49:34 -0700
commitbe231853070770fd7e7c6fd5ca2414e26e7534a7 (patch)
tree1790ece5cd29217b717e85557e40353705e156b2 /drivers/video/fbdev/msm
parent44fa187eeb57e3e03a79337afc4fa4d706285206 (diff)
Merge branch 'mdss-final-replay' into msm-4.4
This merge brings all display changes from msm-3.18 kernel * (58 commits) msm: mdss: add support for additional DMA pipes msm: mdss: refactor device tree pipe parsing logic msm: mdss: refactor mixer configuration code msm: mdss: add support for secure display on msm8953. msm: mdss: disable ECG feature on 28nm PHY platform msm: mdss: send DSI command using TPG when in secure session msm: mdss: Update histogram and PA LUT in mdss V3 msm: mdss: validate layer count before copying userdata msm: mdss: Fix potential NULL pointer dereferences Revert "msm: mdss: Remove redundant handoff pending check" msm: mdss: hdmi: Do not treat intermediate ddc error as failure msm: mdss: revisit igc pipe enumeration logic msm: mdss: Add PA support for mdss V3 msm: mdss: Add support for mdss v3 ops msm: mdss: Update the postprocessing ops using mdss revision msm: mdss: update the caching payload based on mdss version msm: clk: hdmi: add support for atomic update msm: sde: Add v4l2 rotator driver to enable multi-context usecase msm: mdss: refactor pipe type checks msm: mdss: add proper layer zorder validation msm: mdss: stub bus scaling functions if driver is disabled msm: mdss: avoid failure if primary panel pref is not enabled msm: adv7533: add support for clients to read audio block msm: mdss: add lineptr interrupt support for command mode panels msm: mdss: update rotator frame rate in the pipe configuration mdss: msm: Avoid excessive failure logs in igc config msm: mdss: delay dma commands for split-dsi cmd mode panels msm: mdss: enable GDSC before enabling clocks in MDP3 probe mdss: dsi: turn off phy power supply during static screen mdss: dsi: read dsi and phy revision during dsi ctrl probe msm: mdss: Fix memory leak in MDP3 driver msm: mdss: delay overlay start until first update for external msm: mdss: free splash memory for MSM8909w after splash done msm: mdss: hdmi: separate audio from transmitter core msm: mdss: disable dsi burst mode when idle is enabled msm: mdss: remove invalid csc initialization during hw init msm: mdss: dsi: increase dsi error count only for valid errors msm: mdss: remove HIST LUT programming in mdss_hw_init msm: mdss: dsi: ignore error interrupt when mask not set msm: mdss: add support to configure bus scale vectors from dt msm: mdss: unstage the pipe if there is z_order mismatch msm: mdss: squash MDP3 driver changes and SMMU change msm: mdss: Read the bridge chip name and instance id from DTSI msm: mdss: Enable continuous splash on bridge chip msm: mdss: Fix multiple bridge chip usecase msm: mdss: Enable export of mdss interrupt to external driver msm: mdss: rotator: turn off rotator clock in wq release msm: mdss: fix ulps during suspend feature logic clk: msm: mdss: program correct divider for PLL configuration msm: mdss: fix DSI PHY timing configuration logic msm: mdss: hdmi: add support for hdmi simulation msm: mdss: handle race condition in pingpong done counter clk: qcom: mdss: calculate pixel clock for HDMI during handoff msm: mdss: ensure proper dynamic refresh programming for dual DSI msm: mdss: Add fps flag and update blit request version msm: mdss: initialize fb split values during fb probe mdss: mdp: fix rotator compat layer copy msm: mdss: handle DSI ctrl/PHY regulator control properly CRs-Fixed: 1000197 Change-Id: I521519c8abe8eed6924e2fbe3e1a026126582b77 Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev/msm')
-rw-r--r--drivers/video/fbdev/msm/Makefile3
-rw-r--r--drivers/video/fbdev/msm/mdp3.c1288
-rw-r--r--drivers/video/fbdev/msm/mdp3.h81
-rw-r--r--drivers/video/fbdev/msm/mdp3_ctrl.c911
-rw-r--r--drivers/video/fbdev/msm/mdp3_ctrl.h7
-rw-r--r--drivers/video/fbdev/msm/mdp3_dma.c275
-rw-r--r--drivers/video/fbdev/msm/mdp3_dma.h14
-rw-r--r--drivers/video/fbdev/msm/mdp3_hwio.h16
-rw-r--r--drivers/video/fbdev/msm/mdp3_ppp.c527
-rw-r--r--drivers/video/fbdev/msm/mdp3_ppp.h10
-rw-r--r--drivers/video/fbdev/msm/mdp3_ppp_hwio.c212
-rw-r--r--drivers/video/fbdev/msm/mdss.h12
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_dba_utils.c28
-rw-r--r--drivers/video/fbdev/msm/mdss_dba_utils.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c118
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h29
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_clk.c4
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c200
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c34
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c7
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_audio.c525
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_audio.h72
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c187
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c17
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c747
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.h9
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.c28
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c590
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h138
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c427
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h41
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c379
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c113
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c120
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c38
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c246
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.h8
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c38
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_common.c87
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_common.h27
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c78
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp_v3.c737
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_util.c171
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h11
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c57
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator_internal.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_smmu.c6
-rw-r--r--drivers/video/fbdev/msm/msm_dba/adv7533.c45
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c145
53 files changed, 6487 insertions, 2399 deletions
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index fdeea34122d5..c32772babc71 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -24,6 +24,8 @@ obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_mdp_debug.o
mdss-mdp-objs += mdss_mdp_pp_v1_7.o
+mdss-mdp-objs += mdss_mdp_pp_v3.o
+mdss-mdp-objs += mdss_mdp_pp_common.o
ifeq ($(CONFIG_FB_MSM_MDSS),y)
obj-$(CONFIG_DEBUG_FS) += mdss_debug.o mdss_debug_xlog.o
@@ -48,6 +50,7 @@ obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_tx.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp2p2.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_cec.o
+obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_audio.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8334) += mhl_sii8334.o mhl_msc.o
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c
index db50d5cd2054..fd22928353b4 100644
--- a/drivers/video/fbdev/msm/mdp3.c
+++ b/drivers/video/fbdev/msm/mdp3.c
@@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
+#include <linux/dma-buf.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -22,6 +23,7 @@
#include <linux/iommu.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
@@ -45,22 +47,27 @@
#include <linux/qcom_iommu.h>
#include <linux/msm_iommu_domains.h>
+#include <linux/msm_dma_iommu_mapping.h>
+
#include "mdp3.h"
#include "mdss_fb.h"
#include "mdp3_hwio.h"
#include "mdp3_ctrl.h"
#include "mdp3_ppp.h"
#include "mdss_debug.h"
+#include "mdss_smmu.h"
+#include "mdss.h"
#ifndef EXPORT_COMPAT
#define EXPORT_COMPAT(x)
#endif
+#define AUTOSUSPEND_TIMEOUT_MS 100
#define MISR_POLL_SLEEP 2000
#define MISR_POLL_TIMEOUT 32000
#define MDP3_REG_CAPTURED_DSI_PCLK_MASK 1
-#define MDP_CORE_HW_VERSION 0x03050305
+#define MDP_CORE_HW_VERSION 0x03050306
struct mdp3_hw_resource *mdp3_res;
#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
@@ -178,8 +185,7 @@ void mdp3_irq_enable(int type)
pr_debug("mdp3_irq_enable type=%d\n", type);
spin_lock_irqsave(&mdp3_res->irq_lock, flag);
- mdp3_res->irq_ref_count[type] += 1;
- if (mdp3_res->irq_ref_count[type] > 1) {
+ if (mdp3_res->irq_ref_count[type] > 0) {
pr_debug("interrupt %d already enabled\n", type);
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
return;
@@ -188,6 +194,7 @@ void mdp3_irq_enable(int type)
mdp3_res->irq_mask |= BIT(type);
MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irq_mask);
+ mdp3_res->irq_ref_count[type] += 1;
spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
}
@@ -371,7 +378,7 @@ int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota)
for (i = 0; i < MDP3_CLIENT_MAX; i++) {
total_ab += bus_handle->ab[i];
- total_ib += bus_handle->ab[i];
+ total_ib += bus_handle->ib[i];
}
if ((total_ab | total_ib) == 0) {
@@ -487,7 +494,8 @@ int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate,
if (ret)
pr_err("clk_set_rate failed ret=%d\n", ret);
else
- pr_debug("mdp clk rate=%lu\n", rounded_rate);
+ pr_debug("mdp clk rate=%lu, client = %d\n",
+ rounded_rate, client);
}
mutex_unlock(&mdp3_res->res_mutex);
} else {
@@ -585,18 +593,63 @@ static void mdp3_clk_remove(void)
}
+u64 mdp3_clk_round_off(u64 clk_rate)
+{
+ u64 clk_round_off = 0;
+
+ if (clk_rate <= MDP_CORE_CLK_RATE_SVS)
+ clk_round_off = MDP_CORE_CLK_RATE_SVS;
+ else if (clk_rate <= MDP_CORE_CLK_RATE_SUPER_SVS)
+ clk_round_off = MDP_CORE_CLK_RATE_SUPER_SVS;
+ else
+ clk_round_off = MDP_CORE_CLK_RATE_MAX;
+
+ pr_debug("clk = %llu rounded to = %llu\n",
+ clk_rate, clk_round_off);
+ return clk_round_off;
+}
+
int mdp3_clk_enable(int enable, int dsi_clk)
{
- int rc;
+ int rc = 0;
+ int changed = 0;
pr_debug("MDP CLKS %s\n", (enable ? "Enable" : "Disable"));
mutex_lock(&mdp3_res->res_mutex);
+
+ if (enable) {
+ if (mdp3_res->clk_ena == 0)
+ changed++;
+ mdp3_res->clk_ena++;
+ } else {
+ if (mdp3_res->clk_ena) {
+ mdp3_res->clk_ena--;
+ if (mdp3_res->clk_ena == 0)
+ changed++;
+ } else {
+ pr_err("Can not be turned off\n");
+ }
+ }
+ pr_debug("%s: clk_ena=%d changed=%d enable=%d\n",
+ __func__, mdp3_res->clk_ena, changed, enable);
+
+ if (changed) {
+ if (enable)
+ pm_runtime_get_sync(&mdp3_res->pdev->dev);
+
rc = mdp3_clk_update(MDP3_CLK_AHB, enable);
rc |= mdp3_clk_update(MDP3_CLK_AXI, enable);
rc |= mdp3_clk_update(MDP3_CLK_MDP_SRC, enable);
rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, enable);
rc |= mdp3_clk_update(MDP3_CLK_VSYNC, enable);
+
+ if (!enable) {
+ pm_runtime_mark_last_busy(&mdp3_res->pdev->dev);
+ pm_runtime_put_autosuspend(&mdp3_res->pdev->dev);
+ }
+ }
+
mutex_unlock(&mdp3_res->res_mutex);
return rc;
}
@@ -606,7 +659,7 @@ void mdp3_bus_bw_iommu_enable(int enable, int client)
struct mdp3_bus_handle_map *bus_handle;
int client_idx;
u64 ab = 0, ib = 0;
- int ref_cnt, i;
+ int ref_cnt;
client_idx = MDP3_BUS_HANDLE;
@@ -619,27 +672,53 @@ void mdp3_bus_bw_iommu_enable(int enable, int client)
if (enable)
bus_handle->ref_cnt++;
else
- bus_handle->ref_cnt--;
+ if (bus_handle->ref_cnt)
+ bus_handle->ref_cnt--;
ref_cnt = bus_handle->ref_cnt;
mutex_unlock(&mdp3_res->res_mutex);
- if (enable && ref_cnt == 1) {
+ if (enable) {
if (mdp3_res->allow_iommu_update)
- mdp3_iommu_enable();
- for (i = 0; i < MDP3_CLIENT_MAX; i++) {
- ab += bus_handle->restore_ab[i];
- ib += bus_handle->restore_ib[i];
- }
+ mdp3_iommu_enable(client);
+ if (ref_cnt == 1) {
+ pm_runtime_get_sync(&mdp3_res->pdev->dev);
+ ab = bus_handle->restore_ab[client];
+ ib = bus_handle->restore_ib[client];
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();
- } else if (ref_cnt < 0) {
+ }
+ } else {
+ if (ref_cnt == 0) {
+ mdp3_bus_scale_set_quota(client, 0, 0);
+ pm_runtime_mark_last_busy(&mdp3_res->pdev->dev);
+ pm_runtime_put_autosuspend(&mdp3_res->pdev->dev);
+ }
+ mdp3_iommu_disable(client);
+ }
+
+ if (ref_cnt < 0) {
pr_err("Ref count < 0, bus client=%d, ref_cnt=%d",
client_idx, ref_cnt);
}
}
+void mdp3_calc_dma_res(struct mdss_panel_info *panel_info, u64 *clk_rate,
+ u64 *ab, u64 *ib, uint32_t bpp)
+{
+ u32 vtotal = mdss_panel_get_vtotal(panel_info);
+ u32 htotal = mdss_panel_get_htotal(panel_info, 0);
+ u64 clk = htotal * vtotal * panel_info->mipi.frame_rate;
+
+ pr_debug("clk_rate for dma = %llu, bpp = %d\n", clk, bpp);
+ if (clk_rate)
+ *clk_rate = mdp3_clk_round_off(clk);
+
+ /* ab and ib vote should be same for honest voting */
+ if (ab || ib) {
+ *ab = clk * bpp;
+ *ib = *ab;
+ }
+}
+
int mdp3_res_update(int enable, int dsi_clk, int client)
{
int rc = 0;
@@ -681,6 +760,7 @@ int mdp3_get_mdp_dsi_clk(void)
int mdp3_put_mdp_dsi_clk(void)
{
int rc;
+
mutex_lock(&mdp3_res->res_mutex);
rc = mdp3_clk_update(MDP3_CLK_DSI, 0);
mutex_unlock(&mdp3_res->res_mutex);
@@ -701,7 +781,7 @@ static int mdp3_irq_setup(void)
pr_err("mdp request_irq() failed!\n");
return ret;
}
- mdp3_res->mdss_util->disable_irq_nosync(&mdp3_res->mdp3_hw);
+ disable_irq_nosync(mdp3_hw->irq_info->irq);
mdp3_res->irq_registered = true;
return 0;
}
@@ -782,7 +862,7 @@ int mdp3_iommu_domain_init(void)
layout.client_name = mdp3_iommu_domains[i].client_name;
layout.partitions = mdp3_iommu_domains[i].partitions;
layout.npartitions = mdp3_iommu_domains[i].npartitions;
- layout.is_secure = false;
+ layout.is_secure = (i == MDP3_IOMMU_DOMAIN_SECURE);
domain_idx = msm_register_domain(&layout);
if (IS_ERR_VALUE(domain_idx))
@@ -870,24 +950,23 @@ static int mdp3_check_version(void)
{
int rc;
- rc = mdp3_clk_update(MDP3_CLK_AHB, 1);
- rc |= mdp3_clk_update(MDP3_CLK_AXI, 1);
- rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, 1);
- if (rc)
+ rc = mdp3_clk_enable(1, 0);
+ if (rc) {
+ pr_err("fail to turn on MDP core clks\n");
return rc;
+ }
mdp3_res->mdp_rev = MDP3_REG_READ(MDP3_REG_HW_VERSION);
- rc = mdp3_clk_update(MDP3_CLK_AHB, 0);
- rc |= mdp3_clk_update(MDP3_CLK_AXI, 0);
- rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, 0);
- if (rc)
- pr_err("fail to turn off the MDP3_CLK_AHB clk\n");
-
if (mdp3_res->mdp_rev != MDP_CORE_HW_VERSION) {
pr_err("mdp_hw_revision=%x mismatch\n", mdp3_res->mdp_rev);
rc = -ENODEV;
}
+
+ rc = mdp3_clk_enable(0, 0);
+ if (rc)
+ pr_err("fail to turn off MDP core clks\n");
+
return rc;
}
@@ -900,9 +979,11 @@ static int mdp3_hw_init(void)
mdp3_res->dma[i].capability = MDP3_DMA_CAP_ALL;
mdp3_res->dma[i].in_use = 0;
mdp3_res->dma[i].available = 1;
+ mdp3_res->dma[i].cc_vect_sel = 0;
mdp3_res->dma[i].lut_sts = 0;
mdp3_res->dma[i].hist_cmap = NULL;
mdp3_res->dma[i].gc_cmap = NULL;
+ mutex_init(&mdp3_res->dma[i].pp_lock);
}
mdp3_res->dma[MDP3_DMA_S].capability = MDP3_DMA_CAP_DITHER;
mdp3_res->dma[MDP3_DMA_E].available = 0;
@@ -915,10 +996,121 @@ static int mdp3_hw_init(void)
}
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_AHB].available = 0;
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_LCDC].available = 0;
-
+ mdp3_res->smart_blit_en = SMART_BLIT_RGB_EN | SMART_BLIT_YUV_EN;
+ mdp3_res->solid_fill_vote_en = false;
return 0;
}
+int mdp3_dynamic_clock_gating_ctrl(int enable)
+{
+ int rc = 0;
+ int cgc_cfg = 0;
+ /*Disable dynamic auto clock gating*/
+ pr_debug("%s Status %s\n", __func__, (enable ? "ON":"OFF"));
+ rc = mdp3_clk_enable(1, 0);
+ if (rc) {
+ pr_err("fail to turn on MDP core clks\n");
+ return rc;
+ }
+ cgc_cfg = MDP3_REG_READ(MDP3_REG_CGC_EN);
+ if (enable) {
+ cgc_cfg |= (BIT(10));
+ cgc_cfg |= (BIT(18));
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc_cfg);
+ VBIF_REG_WRITE(MDP3_VBIF_REG_FORCE_EN, 0x0);
+ } else {
+ cgc_cfg &= ~(BIT(10));
+ cgc_cfg &= ~(BIT(18));
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc_cfg);
+ VBIF_REG_WRITE(MDP3_VBIF_REG_FORCE_EN, 0x3);
+ }
+
+ rc = mdp3_clk_enable(0, 0);
+ if (rc)
+ pr_err("fail to turn off MDP core clks\n");
+
+ return rc;
+}
+
+/**
+ * mdp3_get_panic_lut_cfg() - calculate panic and robust lut mask
+ * @panel_width: Panel width
+ *
+ * DMA buffer has 16 fill levels. Which needs to configured as safe
+ * and panic levels based on panel resolutions.
+ * No. of fill levels used = ((panel active width * 8) / 512).
+ * Roundoff the fill levels if needed.
+ * half of the total fill levels used will be treated as panic levels.
+ * Roundoff panic levels if total used fill levels are odd.
+ *
+ * Sample calculation for 720p display:
+ * Fill levels used = (720 * 8) / 512 = 12.5 after round off 13.
+ * panic levels = 13 / 2 = 6.5 after roundoff 7.
+ * Panic mask = 0x3FFF (2 bits per level)
+ * Robust mask = 0xFF80 (1 bit per level)
+ */
+u64 mdp3_get_panic_lut_cfg(u32 panel_width)
+{
+ u32 fill_levels = (((panel_width * 8) / 512) + 1);
+ u32 panic_mask = 0;
+ u32 robust_mask = 0;
+ u32 i = 0;
+ u64 panic_config = 0;
+ u32 panic_levels = 0;
+
+ panic_levels = fill_levels / 2;
+ if (fill_levels % 2)
+ panic_levels++;
+
+ for (i = 0; i < panic_levels; i++) {
+ panic_mask |= (BIT((i * 2) + 1) | BIT(i * 2));
+ robust_mask |= BIT(i);
+ }
+ panic_config = ~robust_mask;
+ panic_config = panic_config << 32;
+ panic_config |= panic_mask;
+ return panic_config;
+}
+
+int mdp3_qos_remapper_setup(struct mdss_panel_data *panel)
+{
+ int rc = 0;
+ u64 panic_config = mdp3_get_panic_lut_cfg(panel->panel_info.xres);
+
+ rc = mdp3_clk_update(MDP3_CLK_AHB, 1);
+ rc |= mdp3_clk_update(MDP3_CLK_AXI, 1);
+ rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, 1);
+ if (rc) {
+ pr_err("fail to turn on MDP core clks\n");
+ return rc;
+ }
+
+ if (!panel)
+ return -EINVAL;
+ /* Program MDP QOS Remapper */
+ MDP3_REG_WRITE(MDP3_DMA_P_QOS_REMAPPER, 0x1A9);
+ MDP3_REG_WRITE(MDP3_DMA_P_WATERMARK_0, 0x0);
+ MDP3_REG_WRITE(MDP3_DMA_P_WATERMARK_1, 0x0);
+ MDP3_REG_WRITE(MDP3_DMA_P_WATERMARK_2, 0x0);
+ /* PANIC setting depends on panel width*/
+ MDP3_REG_WRITE(MDP3_PANIC_LUT0, (panic_config & 0xFFFF));
+ MDP3_REG_WRITE(MDP3_PANIC_LUT1, ((panic_config >> 16) & 0xFFFF));
+ MDP3_REG_WRITE(MDP3_ROBUST_LUT, ((panic_config >> 32) & 0xFFFF));
+ MDP3_REG_WRITE(MDP3_PANIC_ROBUST_CTRL, 0x1);
+ pr_debug("Panel width %d Panic Lut0 %x Lut1 %x Robust %x\n",
+ panel->panel_info.xres,
+ MDP3_REG_READ(MDP3_PANIC_LUT0),
+ MDP3_REG_READ(MDP3_PANIC_LUT1),
+ MDP3_REG_READ(MDP3_ROBUST_LUT));
+
+ rc = mdp3_clk_update(MDP3_CLK_AHB, 0);
+ rc |= mdp3_clk_update(MDP3_CLK_AXI, 0);
+ rc |= mdp3_clk_update(MDP3_CLK_MDP_CORE, 0);
+ if (rc)
+ pr_err("fail to turn off MDP core clks\n");
+ return rc;
+}
+
static int mdp3_res_init(void)
{
int rc = 0;
@@ -958,12 +1150,14 @@ static int mdp3_res_init(void)
static void mdp3_res_deinit(void)
{
struct mdss_hw *mdp3_hw;
+ int i;
mdp3_hw = &mdp3_res->mdp3_hw;
mdp3_bus_scale_unregister();
mutex_lock(&mdp3_res->iommu_lock);
- mdp3_iommu_dettach(MDP3_IOMMU_CTX_MDP_0);
+ for (i = 0; i < MDP3_IOMMU_CTX_MAX; i++)
+ mdp3_iommu_dettach(i);
mutex_unlock(&mdp3_res->iommu_lock);
mdp3_iommu_deinit();
@@ -1154,6 +1348,24 @@ static int mdp3_parse_dt(struct platform_device *pdev)
(int) res->start,
(int) mdp3_res->mdp_base);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vbif_phys");
+ if (!res) {
+ pr_err("unable to get VBIF base address\n");
+ return -EINVAL;
+ }
+
+ mdp3_res->vbif_reg_size = resource_size(res);
+ mdp3_res->vbif_base = devm_ioremap(&pdev->dev, res->start,
+ mdp3_res->vbif_reg_size);
+ if (unlikely(!mdp3_res->vbif_base)) {
+ pr_err("unable to map VBIF base\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("VBIF HW Base phy_Address=0x%x virt=0x%x\n",
+ (int) res->start,
+ (int) mdp3_res->vbif_base);
+
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
pr_err("unable to get MDSS irq\n");
@@ -1181,6 +1393,9 @@ static int mdp3_parse_dt(struct platform_device *pdev)
pdev->dev.of_node, "qcom,mdss-has-panic-ctrl");
mdp3_res->dma[MDP3_DMA_P].has_panic_ctrl = panic_ctrl;
+ mdp3_res->idle_pc_enabled = of_property_read_bool(
+ pdev->dev.of_node, "qcom,mdss-idle-power-collapse-enabled");
+
return 0;
}
@@ -1270,39 +1485,364 @@ void mdp3_enable_regulator(int enable)
mdp3_batfet_ctrl(enable);
}
-int mdp3_put_img(struct mdp3_img_data *data)
+static void mdp3_iommu_heap_unmap_iommu(struct mdp3_iommu_meta *meta)
+{
+ unsigned int domain_num;
+ unsigned int partition_num = 0;
+ struct iommu_domain *domain;
+
+ domain_num = (mdp3_res->domains +
+ MDP3_IOMMU_DOMAIN_UNSECURE)->domain_idx;
+ domain = msm_get_iommu_domain(domain_num);
+
+ if (!domain) {
+ pr_err("Could not get domain %d. Corruption?\n", domain_num);
+ return;
+ }
+
+ iommu_unmap_range(domain, meta->iova_addr, meta->mapped_size);
+ msm_free_iova_address(meta->iova_addr, domain_num, partition_num,
+ meta->mapped_size);
+}
+
+static void mdp3_iommu_meta_destroy(struct kref *kref)
+{
+ struct mdp3_iommu_meta *meta =
+ container_of(kref, struct mdp3_iommu_meta, ref);
+
+ rb_erase(&meta->node, &mdp3_res->iommu_root);
+ mdp3_iommu_heap_unmap_iommu(meta);
+ dma_buf_put(meta->dbuf);
+ kfree(meta);
+}
+
+
+static void mdp3_iommu_meta_put(struct mdp3_iommu_meta *meta)
+{
+ /* Need to lock here to prevent race against map/unmap */
+ mutex_lock(&mdp3_res->iommu_lock);
+ kref_put(&meta->ref, mdp3_iommu_meta_destroy);
+ mutex_unlock(&mdp3_res->iommu_lock);
+}
+
+static struct mdp3_iommu_meta *mdp3_iommu_meta_lookup(struct sg_table *table)
+{
+ struct rb_root *root = &mdp3_res->iommu_root;
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct mdp3_iommu_meta *entry = NULL;
+
+ while (*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct mdp3_iommu_meta, node);
+
+ if (table < entry->table)
+ p = &(*p)->rb_left;
+ else if (table > entry->table)
+ p = &(*p)->rb_right;
+ else
+ return entry;
+ }
+ return NULL;
+}
+
+void mdp3_unmap_iommu(struct ion_client *client, struct ion_handle *handle)
+{
+ struct mdp3_iommu_meta *meta;
+ struct sg_table *table;
+
+ table = ion_sg_table(client, handle);
+
+ mutex_lock(&mdp3_res->iommu_lock);
+ meta = mdp3_iommu_meta_lookup(table);
+ if (!meta) {
+ WARN(1, "%s: buffer was never mapped for %p\n", __func__,
+ handle);
+ mutex_unlock(&mdp3_res->iommu_lock);
+ return;
+ }
+ mutex_unlock(&mdp3_res->iommu_lock);
+
+ mdp3_iommu_meta_put(meta);
+}
+
+static void mdp3_iommu_meta_add(struct mdp3_iommu_meta *meta)
+{
+ struct rb_root *root = &mdp3_res->iommu_root;
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct mdp3_iommu_meta *entry;
+
+ while (*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct mdp3_iommu_meta, node);
+
+ if (meta->table < entry->table) {
+ p = &(*p)->rb_left;
+ } else if (meta->table > entry->table) {
+ p = &(*p)->rb_right;
+ } else {
+ pr_err("%s: handle %p already exists\n", __func__,
+ entry->handle);
+ BUG();
+ }
+ }
+
+ rb_link_node(&meta->node, parent, p);
+ rb_insert_color(&meta->node, root);
+}
+
+static int mdp3_iommu_map_iommu(struct mdp3_iommu_meta *meta,
+ unsigned long align, unsigned long iova_length,
+ unsigned int padding, unsigned long flags)
+{
+ struct iommu_domain *domain;
+ int ret = 0;
+ unsigned long size;
+ unsigned long unmap_size;
+ struct sg_table *table;
+ int prot = IOMMU_WRITE | IOMMU_READ;
+ unsigned int domain_num = (mdp3_res->domains +
+ MDP3_IOMMU_DOMAIN_UNSECURE)->domain_idx;
+ unsigned int partition_num = 0;
+
+ size = meta->size;
+ table = meta->table;
+
+ /* Use the biggest alignment to allow bigger IOMMU mappings.
+ * Use the first entry since the first entry will always be the
+ * biggest entry. To take advantage of bigger mapping sizes both the
+ * VA and PA addresses have to be aligned to the biggest size.
+ */
+ if (table->sgl->length > align)
+ align = table->sgl->length;
+
+ ret = msm_allocate_iova_address(domain_num, partition_num,
+ meta->mapped_size, align,
+ (unsigned long *)&meta->iova_addr);
+
+ if (ret)
+ goto out;
+
+ domain = msm_get_iommu_domain(domain_num);
+
+ if (!domain) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ /* Adding padding to before buffer */
+ if (padding) {
+ unsigned long phys_addr = sg_phys(table->sgl);
+
+ ret = msm_iommu_map_extra(domain, meta->iova_addr, phys_addr,
+ padding, SZ_4K, prot);
+ if (ret)
+ goto out1;
+ }
+
+ /* Mapping actual buffer */
+ ret = iommu_map_range(domain, meta->iova_addr + padding,
+ table->sgl, size, prot);
+ if (ret) {
+ pr_err("%s: could not map %pa in domain %p\n",
+ __func__, &meta->iova_addr, domain);
+ unmap_size = padding;
+ goto out2;
+ }
+
+ /* Adding padding to end of buffer */
+ if (padding) {
+ unsigned long phys_addr = sg_phys(table->sgl);
+ unsigned long extra_iova_addr = meta->iova_addr +
+ padding + size;
+ ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
+ padding, SZ_4K, prot);
+ if (ret) {
+ unmap_size = padding + size;
+ goto out2;
+ }
+ }
+ return ret;
+
+out2:
+ iommu_unmap_range(domain, meta->iova_addr, unmap_size);
+out1:
+ msm_free_iova_address(meta->iova_addr, domain_num, partition_num,
+ iova_length);
+
+out:
+ return ret;
+}
+
+static struct mdp3_iommu_meta *mdp3_iommu_meta_create(struct ion_client *client,
+ struct ion_handle *handle, struct sg_table *table, unsigned long size,
+ unsigned long align, unsigned long iova_length, unsigned int padding,
+ unsigned long flags, dma_addr_t *iova)
+{
+ struct mdp3_iommu_meta *meta;
+ int ret;
+
+ meta = kzalloc(sizeof(*meta), GFP_KERNEL);
+
+ if (!meta)
+ return ERR_PTR(-ENOMEM);
+
+ meta->handle = handle;
+ meta->table = table;
+ meta->size = size;
+ meta->mapped_size = iova_length;
+ meta->dbuf = ion_share_dma_buf(client, handle);
+ kref_init(&meta->ref);
+
+ ret = mdp3_iommu_map_iommu(meta,
+ align, iova_length, padding, flags);
+ if (ret < 0) {
+ pr_err("%s: Unable to map buffer\n", __func__);
+ goto out;
+ }
+
+ *iova = meta->iova_addr;
+ mdp3_iommu_meta_add(meta);
+
+ return meta;
+out:
+ kfree(meta);
+ return ERR_PTR(ret);
+}
+
+/*
+ * PPP hw reads in tiles of 16 which might be outside mapped region
+ * need to map buffers ourseleve to add extra padding
+ */
+int mdp3_self_map_iommu(struct ion_client *client, struct ion_handle *handle,
+ unsigned long align, unsigned long padding, dma_addr_t *iova,
+ unsigned long *buffer_size, unsigned long flags,
+ unsigned long iommu_flags)
+{
+ struct mdp3_iommu_meta *iommu_meta = NULL;
+ struct sg_table *table;
+ struct scatterlist *sg;
+ unsigned long size = 0, iova_length = 0;
+ int ret = 0;
+ int i;
+
+ table = ion_sg_table(client, handle);
+ if (IS_ERR_OR_NULL(table))
+ return PTR_ERR(table);
+
+ for_each_sg(table->sgl, sg, table->nents, i)
+ size += sg->length;
+
+ padding = PAGE_ALIGN(padding);
+
+ /* Adding 16 lines padding before and after buffer */
+ iova_length = size + 2 * padding;
+
+ if (size & ~PAGE_MASK) {
+ pr_debug("%s: buffer size %lx is not aligned to %lx",
+ __func__, size, PAGE_SIZE);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (iova_length & ~PAGE_MASK) {
+ pr_debug("%s: iova_length %lx is not aligned to %lx",
+ __func__, iova_length, PAGE_SIZE);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&mdp3_res->iommu_lock);
+ iommu_meta = mdp3_iommu_meta_lookup(table);
+
+ if (!iommu_meta) {
+ iommu_meta = mdp3_iommu_meta_create(client, handle, table, size,
+ align, iova_length, padding, flags, iova);
+ if (!IS_ERR_OR_NULL(iommu_meta)) {
+ iommu_meta->flags = iommu_flags;
+ ret = 0;
+ } else {
+ ret = PTR_ERR(iommu_meta);
+ goto out_unlock;
+ }
+ } else {
+ if (iommu_meta->flags != iommu_flags) {
+ pr_err("%s: hndl %p already mapped with diff flag\n",
+ __func__, handle);
+ ret = -EINVAL;
+ goto out_unlock;
+ } else if (iommu_meta->mapped_size != iova_length) {
+ pr_err("%s: hndl %p already mapped with diff len\n",
+ __func__, handle);
+ ret = -EINVAL;
+ goto out_unlock;
+ } else {
+ kref_get(&iommu_meta->ref);
+ *iova = iommu_meta->iova_addr;
+ }
+ }
+ BUG_ON(iommu_meta->size != size);
+ mutex_unlock(&mdp3_res->iommu_lock);
+
+ *iova = *iova + padding;
+ *buffer_size = size;
+ return ret;
+
+out_unlock:
+ mutex_unlock(&mdp3_res->iommu_lock);
+out:
+ mdp3_iommu_meta_put(iommu_meta);
+ return ret;
+}
+
+int mdp3_put_img(struct mdp3_img_data *data, int client)
{
struct ion_client *iclient = mdp3_res->ion_client;
int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN_UNSECURE)->domain_idx;
+ int dir = DMA_BIDIRECTIONAL;
- if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
+ if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
pr_info("mdp3_put_img fb mem buf=0x%pa\n", &data->addr);
fdput(data->srcp_f);
memset(&data->srcp_f, 0, sizeof(struct fd));
- } else if (!IS_ERR_OR_NULL(data->srcp_ihdl)) {
- ion_unmap_iommu(iclient, data->srcp_ihdl, dom, 0);
- ion_free(iclient, data->srcp_ihdl);
- data->srcp_ihdl = NULL;
+ } else if (!IS_ERR_OR_NULL(data->srcp_dma_buf)) {
+ pr_debug("ion hdl = %p buf=0x%pa\n", data->srcp_dma_buf,
+ &data->addr);
+ if (!iclient) {
+ pr_err("invalid ion client\n");
+ return -ENOMEM;
+ }
+ if (data->mapped) {
+ mdss_smmu_unmap_dma_buf(data->srcp_table,
+ dom, dir,
+ data->srcp_dma_buf);
+ data->mapped = false;
+ }
+ if (!data->skip_detach) {
+ dma_buf_unmap_attachment(data->srcp_attachment,
+ data->srcp_table,
+ mdss_smmu_dma_data_direction(dir));
+ dma_buf_detach(data->srcp_dma_buf,
+ data->srcp_attachment);
+ dma_buf_put(data->srcp_dma_buf);
+ data->srcp_dma_buf = NULL;
+ }
} else {
return -EINVAL;
}
return 0;
}
-int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data)
+int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client)
{
struct fd f;
int ret = -EINVAL;
int fb_num;
- unsigned long *len;
- dma_addr_t *start;
struct ion_client *iclient = mdp3_res->ion_client;
int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN_UNSECURE)->domain_idx;
- start = &data->addr;
- len = (unsigned long *) &data->len;
data->flags = img->flags;
- data->p_need = 0;
if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
f = fdget(img->memory_id);
@@ -1313,7 +1853,8 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data)
}
if (MAJOR(f.file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
fb_num = MINOR(f.file->f_dentry->d_inode->i_rdev);
- ret = mdss_fb_get_phys_info(start, len, fb_num);
+ ret = mdss_fb_get_phys_info(&data->addr,
+ &data->len, fb_num);
if (ret) {
pr_err("mdss_fb_get_phys_info() failed\n");
fdput(f);
@@ -1328,125 +1869,126 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data)
if (!ret)
goto done;
} else if (iclient) {
- data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id);
- if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
- pr_err("error on ion_import_fd\n");
- if (!data->srcp_ihdl)
- ret = -EINVAL;
- else
- ret = PTR_ERR(data->srcp_ihdl);
- data->srcp_ihdl = NULL;
- return ret;
- }
- ret = ion_map_iommu(iclient, data->srcp_ihdl, dom,
- 0, SZ_4K, 0, start, len, 0, 0);
- if (IS_ERR_VALUE(ret)) {
- ion_free(iclient, data->srcp_ihdl);
- pr_err("failed to map ion handle (%d)\n", ret);
- return ret;
- }
+ data->srcp_dma_buf = dma_buf_get(img->memory_id);
+ if (IS_ERR(data->srcp_dma_buf)) {
+ pr_err("DMA : error on ion_import_fd\n");
+ ret = PTR_ERR(data->srcp_dma_buf);
+ data->srcp_dma_buf = NULL;
+ return ret;
+ }
+
+ data->srcp_attachment =
+ mdss_smmu_dma_buf_attach(data->srcp_dma_buf,
+ &mdp3_res->pdev->dev, dom);
+ if (IS_ERR(data->srcp_attachment)) {
+ ret = PTR_ERR(data->srcp_attachment);
+ goto err_put;
+ }
+
+ data->srcp_table =
+ dma_buf_map_attachment(data->srcp_attachment,
+ mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL));
+ if (IS_ERR(data->srcp_table)) {
+ ret = PTR_ERR(data->srcp_table);
+ goto err_detach;
+ }
+
+ ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf,
+ data->srcp_table, dom,
+ &data->addr, &data->len, DMA_BIDIRECTIONAL);
+
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("smmu map dma buf failed: (%d)\n", ret);
+ goto err_unmap;
+ }
+
+ data->mapped = true;
+ data->skip_detach = false;
}
done:
if (!ret && (img->offset < data->len)) {
data->addr += img->offset;
data->len -= img->offset;
- pr_debug("mem=%d ihdl=%p buf=0x%pa len=0x%x\n", img->memory_id,
- data->srcp_ihdl, &data->addr, data->len);
+ pr_debug("mem=%d ihdl=%p buf=0x%pa len=0x%lx\n",
+ img->memory_id, data->srcp_dma_buf,
+ &data->addr, data->len);
+
} else {
- mdp3_put_img(data);
+ mdp3_put_img(data, client);
return -EINVAL;
}
+ return ret;
+err_detach:
+ dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+err_put:
+ dma_buf_put(data->srcp_dma_buf);
+ return ret;
+err_unmap:
+ dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
+ mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL));
+ dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
+ dma_buf_put(data->srcp_dma_buf);
return ret;
+
}
-int mdp3_iommu_enable()
+int mdp3_iommu_enable(int client)
{
- int i, rc = 0;
+ int rc = 0;
mutex_lock(&mdp3_res->iommu_lock);
+
if (mdp3_res->iommu_ref_cnt == 0) {
- for (i = 0; i < MDP3_IOMMU_CTX_MAX; i++) {
- rc = mdp3_iommu_attach(i);
- if (rc) {
- WARN(1, "IOMMU attach failed for ctx: %d\n", i);
- for (i--; i >= 0; i--)
- mdp3_iommu_dettach(i);
- }
- }
+ rc = mdss_smmu_attach(mdss_res);
+ if (rc)
+ rc = mdss_smmu_detach(mdss_res);
}
if (!rc)
mdp3_res->iommu_ref_cnt++;
mutex_unlock(&mdp3_res->iommu_lock);
+ pr_debug("client :%d total_ref_cnt: %d\n",
+ client, mdp3_res->iommu_ref_cnt);
return rc;
}
-int mdp3_iommu_disable()
+int mdp3_iommu_disable(int client)
{
- int i, rc = 0;
+ int rc = 0;
mutex_lock(&mdp3_res->iommu_lock);
if (mdp3_res->iommu_ref_cnt) {
mdp3_res->iommu_ref_cnt--;
- if (mdp3_res->iommu_ref_cnt == 0) {
- for (i = 0; i < MDP3_IOMMU_CTX_MAX; i++)
- rc = mdp3_iommu_dettach(i);
- }
+
+ pr_debug("client :%d total_ref_cnt: %d\n",
+ client, mdp3_res->iommu_ref_cnt);
+ if (mdp3_res->iommu_ref_cnt == 0)
+ rc = mdss_smmu_detach(mdss_res);
} else {
- pr_err("iommu ref count unbalanced\n");
+ pr_err("iommu ref count unbalanced for client %d\n", client);
}
mutex_unlock(&mdp3_res->iommu_lock);
return rc;
}
-int mdp3_panel_get_intf_status(u32 disp_num, u32 intf_type)
-{
- int rc = 0, status = 0;
-
- if (intf_type != MDSS_PANEL_INTF_DSI)
- return 0;
-
- mdp3_clk_update(MDP3_CLK_AHB, 1);
- mdp3_clk_update(MDP3_CLK_AXI, 1);
- mdp3_clk_update(MDP3_CLK_MDP_CORE, 1);
-
- status = (MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG) & 0x180000);
- /* DSI video mode or command mode */
- rc = (status == 0x180000) || (status == 0x080000);
-
- mdp3_clk_update(MDP3_CLK_AHB, 0);
- mdp3_clk_update(MDP3_CLK_AXI, 0);
- mdp3_clk_update(MDP3_CLK_MDP_CORE, 0);
-
- return rc;
-}
-
int mdp3_iommu_ctrl(int enable)
{
int rc;
+ if (mdp3_res->allow_iommu_update == false)
+ return 0;
+
if (enable)
- rc = mdp3_iommu_enable();
+ rc = mdp3_iommu_enable(MDP3_CLIENT_DSI);
else
- rc = mdp3_iommu_disable();
+ rc = mdp3_iommu_disable(MDP3_CLIENT_DSI);
return rc;
}
-int mdp3_iommu_is_attached()
-{
- struct mdp3_iommu_ctx_map *context_map;
-
- if (!mdp3_res->iommu_contexts)
- return 0;
-
- context_map = mdp3_res->iommu_contexts + MDP3_IOMMU_CTX_MDP_0;
- return context_map->attached;
-}
-
static int mdp3_init(struct msm_fb_data_type *mfd)
{
int rc;
@@ -1479,57 +2021,124 @@ u32 mdp3_fb_stride(u32 fb_index, u32 xres, int bpp)
return xres * bpp;
}
+__ref int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd)
+{
+ struct platform_device *pdev = mfd->pdev;
+ int len = 0, rc = 0;
+ u32 offsets[2];
+ struct device_node *pnode, *child_node;
+ struct property *prop = NULL;
+
+ mfd->splash_info.splash_logo_enabled =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mdss-fb-splash-logo-enabled");
+
+ prop = of_find_property(pdev->dev.of_node, "qcom,memblock-reserve",
+ &len);
+ if (!prop) {
+ pr_debug("Read memblock reserve settings for fb failed\n");
+ pr_debug("Read cont-splash-memory settings\n");
+ }
+
+ if (len) {
+ len = len / sizeof(u32);
+
+ rc = of_property_read_u32_array(pdev->dev.of_node,
+ "qcom,memblock-reserve", offsets, len);
+ if (rc) {
+ pr_err("error reading mem reserve settings for fb\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ child_node = of_get_child_by_name(pdev->dev.of_node,
+ "qcom,cont-splash-memory");
+ if (!child_node) {
+ pr_err("splash mem child node is not present\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ pnode = of_parse_phandle(child_node, "linux,contiguous-region",
+ 0);
+ if (pnode != NULL) {
+ const u32 *addr;
+ u64 size;
+
+ addr = of_get_address(pnode, 0, &size, NULL);
+ if (!addr) {
+ pr_err("failed to parse the splash memory address\n");
+ of_node_put(pnode);
+ rc = -EINVAL;
+ goto error;
+ }
+ offsets[0] = (u32) of_read_ulong(addr, 2);
+ offsets[1] = (u32) size;
+ of_node_put(pnode);
+ } else {
+ pr_err("mem reservation for splash screen fb not present\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (!memblock_is_reserved(offsets[0])) {
+ pr_debug("failed to reserve memory for fb splash\n");
+ rc = -EINVAL;
+ goto error;
+ }
+
+ mfd->fbi->fix.smem_start = offsets[0];
+ mfd->fbi->fix.smem_len = offsets[1];
+ mdp3_res->splash_mem_addr = mfd->fbi->fix.smem_start;
+ mdp3_res->splash_mem_size = mfd->fbi->fix.smem_len;
+
+error:
+ if (rc && mfd->panel_info->cont_splash_enabled)
+ pr_err("no rsvd mem found in DT for splash screen\n");
+ else
+ rc = 0;
+
+ return rc;
+}
+
static int mdp3_alloc(struct msm_fb_data_type *mfd)
{
int ret;
int dom;
void *virt;
- unsigned long phys;
- u32 offsets[2];
+ phys_addr_t phys;
size_t size;
- struct platform_device *pdev = mfd->pdev;
mfd->fbi->screen_base = NULL;
mfd->fbi->fix.smem_start = 0;
mfd->fbi->fix.smem_len = 0;
- ret = of_property_read_u32_array(pdev->dev.of_node,
- "qcom,memblock-reserve", offsets, 2);
-
- if (ret) {
- pr_err("fail to parse splash memory address\n");
- return ret;
- }
+ mdp3_parse_dt_splash(mfd);
- phys = offsets[0];
- size = PAGE_ALIGN(mfd->fbi->fix.line_length *
- mfd->fbi->var.yres_virtual);
+ size = mfd->fbi->fix.smem_len;
- if (size > offsets[1]) {
- pr_err("reserved splash memory size too small\n");
- return -EINVAL;
- }
+ dom = mdp3_res->domains[MDP3_IOMMU_DOMAIN_UNSECURE].domain_idx;
- virt = phys_to_virt(phys);
- if (unlikely(!virt)) {
- pr_err("unable to map in splash memory\n");
+ ret = mdss_smmu_dma_alloc_coherent(&mdp3_res->pdev->dev, size,
+ &phys, &mfd->iova, &virt, GFP_KERNEL, dom);
+ if (ret) {
+ pr_err("unable to alloc fbmem size=%zx\n", size);
return -ENOMEM;
}
- dom = mdp3_res->domains[MDP3_IOMMU_DOMAIN_UNSECURE].domain_idx;
- ret = msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0,
- &mfd->iova);
-
- if (ret) {
- pr_err("fail to map to IOMMU %d\n", ret);
- return ret;
+ if (MDSS_LPAE_CHECK(phys)) {
+ pr_warn("fb mem phys %pa > 4GB is not supported.\n", &phys);
+ mdss_smmu_dma_free_coherent(&mdp3_res->pdev->dev, size, &virt,
+ phys, mfd->iova, dom);
+ return -ERANGE;
}
- pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
- size, virt, phys, mfd->index);
- mfd->fbi->screen_base = virt;
+ 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->fix.smem_start = phys;
- mfd->fbi->fix.smem_len = size;
+ mfd->fbi->screen_base = virt;
return 0;
}
@@ -1538,6 +2147,7 @@ void mdp3_free(struct msm_fb_data_type *mfd)
{
size_t size = 0;
int dom;
+ unsigned long phys;
if (!mfd->iova || !mfd->fbi->screen_base) {
pr_info("no fbmem allocated\n");
@@ -1545,41 +2155,17 @@ void mdp3_free(struct msm_fb_data_type *mfd)
}
size = mfd->fbi->fix.smem_len;
+ phys = mfd->fbi->fix.smem_start;
dom = mdp3_res->domains[MDP3_IOMMU_DOMAIN_UNSECURE].domain_idx;
+ iommu_unmap(mdp3_res->domains[MDP3_IOMMU_DOMAIN_UNSECURE].domain,
+ phys, size);
msm_iommu_unmap_contig_buffer(mfd->iova, dom, 0, size);
mfd->fbi->screen_base = NULL;
mfd->fbi->fix.smem_start = 0;
- mfd->fbi->fix.smem_len = 0;
mfd->iova = 0;
}
-int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd)
-{
- struct platform_device *pdev = mfd->pdev;
- int rc;
- u32 offsets[2];
-
- rc = of_property_read_u32_array(pdev->dev.of_node,
- "qcom,memblock-reserve", offsets, 2);
-
- if (rc) {
- pr_err("fail to get memblock-reserve property\n");
- return rc;
- }
-
- if (mdp3_res->splash_mem_addr != offsets[0])
- rc = -EINVAL;
-
- mdp3_res->splash_mem_addr = offsets[0];
- mdp3_res->splash_mem_size = offsets[1];
-
- pr_debug("memaddr=%lx size=%x\n", mdp3_res->splash_mem_addr,
- mdp3_res->splash_mem_size);
-
- return rc;
-}
-
void mdp3_release_splash_memory(struct msm_fb_data_type *mfd)
{
/* Give back the reserved memory to the system */
@@ -1639,10 +2225,11 @@ static int mdp3_is_display_on(struct mdss_panel_data *pdata)
int rc = 0;
u32 status;
- mdp3_clk_update(MDP3_CLK_AHB, 1);
- mdp3_clk_update(MDP3_CLK_AXI, 1);
- mdp3_clk_update(MDP3_CLK_MDP_CORE, 1);
-
+ rc = mdp3_clk_enable(1, 0);
+ if (rc) {
+ pr_err("fail to turn on MDP core clks\n");
+ return rc;
+ }
if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
status = MDP3_REG_READ(MDP3_REG_DSI_VIDEO_EN);
rc = status & 0x1;
@@ -1654,9 +2241,9 @@ static int mdp3_is_display_on(struct mdss_panel_data *pdata)
mdp3_res->splash_mem_addr = MDP3_REG_READ(MDP3_REG_DMA_P_IBUF_ADDR);
- mdp3_clk_update(MDP3_CLK_AHB, 0);
- mdp3_clk_update(MDP3_CLK_AXI, 0);
- mdp3_clk_update(MDP3_CLK_MDP_CORE, 0);
+ rc = mdp3_clk_enable(0, 0);
+ if (rc)
+ pr_err("fail to turn off MDP core clks\n");
return rc;
}
@@ -1664,26 +2251,25 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata)
{
struct mdss_panel_info *panel_info = &pdata->panel_info;
struct mdp3_bus_handle_map *bus_handle;
- u64 ab, ib;
- int rc;
+ u64 ab = 0;
+ u64 ib = 0;
+ u64 mdp_clk_rate = 0;
+ int rc = 0;
pr_debug("mdp3__continuous_splash_on\n");
- mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE,
- MDP3_CLIENT_DMA_P);
-
- mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, MDP_CORE_CLK_RATE_SVS,
- MDP3_CLIENT_DMA_P);
-
bus_handle = &mdp3_res->bus_handle[MDP3_BUS_HANDLE];
if (bus_handle->handle < 1) {
pr_err("invalid bus handle %d\n", bus_handle->handle);
return -EINVAL;
}
+ mdp3_calc_dma_res(panel_info, &mdp_clk_rate, &ab, &ib, panel_info->bpp);
+
+ mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE,
+ MDP3_CLIENT_DMA_P);
+ mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate,
+ MDP3_CLIENT_DMA_P);
- 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[MDP3_CLIENT_DMA_P] = ab;
bus_handle->restore_ib[MDP3_CLIENT_DMA_P] = ib;
@@ -1700,15 +2286,6 @@ static int mdp3_continuous_splash_on(struct mdss_panel_data *pdata)
goto splash_on_err;
}
- if (pdata->event_handler) {
- rc = pdata->event_handler(pdata, MDSS_EVENT_CONT_SPLASH_BEGIN,
- NULL);
- if (rc) {
- pr_err("MDSS_EVENT_CONT_SPLASH_BEGIN event fail\n");
- goto splash_on_err;
- }
- }
-
if (panel_info->type == MIPI_VIDEO_PANEL)
mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_DSI_VIDEO].active = 1;
else
@@ -1729,6 +2306,15 @@ static int mdp3_panel_register_done(struct mdss_panel_data *pdata)
{
int rc = 0;
+ /*
+ * If idle pc feature is not enabled, then get a reference to the
+ * runtime device which will be released when device is turned off
+ */
+ if (!mdp3_res->idle_pc_enabled ||
+ pdata->panel_info.type != MIPI_CMD_PANEL) {
+ pm_runtime_get_sync(&mdp3_res->pdev->dev);
+ }
+
if (pdata->panel_info.cont_splash_enabled) {
if (!mdp3_is_display_on(pdata)) {
pr_err("continuous splash, but bootloader is not\n");
@@ -1746,10 +2332,35 @@ static int mdp3_panel_register_done(struct mdss_panel_data *pdata)
* continue splash screen. This would have happened in
* res_update in continuous_splash_on without this flag.
*/
- mdp3_res->allow_iommu_update = true;
+ if (pdata->panel_info.cont_splash_enabled == false)
+ mdp3_res->allow_iommu_update = true;
+
return rc;
}
+/* mdp3_autorefresh_disable() - Disable Auto refresh
+ * @ panel_info : pointer to panel configuration structure
+ *
+ * This function displable Auto refresh block for command mode panel.
+ */
+int mdp3_autorefresh_disable(struct mdss_panel_info *panel_info)
+{
+ if ((panel_info->type == MIPI_CMD_PANEL) &&
+ (MDP3_REG_READ(MDP3_REG_AUTOREFRESH_CONFIG_P)))
+ MDP3_REG_WRITE(MDP3_REG_AUTOREFRESH_CONFIG_P, 0);
+ return 0;
+}
+
+int mdp3_splash_done(struct mdss_panel_info *panel_info)
+{
+ if (panel_info->cont_splash_enabled) {
+ pr_err("continuous splash is on and splash done called\n");
+ return -EINVAL;
+ }
+ mdp3_res->allow_iommu_update = true;
+ return 0;
+}
+
static int mdp3_debug_dump_stats_show(struct seq_file *s, void *v)
{
struct mdp3_hw_resource *res = (struct mdp3_hw_resource *)s->private;
@@ -1780,6 +2391,13 @@ static int mdp3_debug_init(struct platform_device *pdev)
return -ENOMEM;
mdss_res = mdata;
+ mutex_init(&mdata->reg_lock);
+ mutex_init(&mdata->reg_bus_lock);
+ mutex_init(&mdata->bus_lock);
+ INIT_LIST_HEAD(&mdata->reg_bus_clist);
+ atomic_set(&mdata->sd_client_count, 0);
+ atomic_set(&mdata->active_intf_cnt, 0);
+ mdss_res->mdss_util = mdp3_res->mdss_util;
mdata->debug_inf.debug_enable_clock = mdp3_debug_enable_clock;
@@ -1811,9 +2429,17 @@ static void mdp3_debug_deinit(struct platform_device *pdev)
static void mdp3_dma_underrun_intr_handler(int type, void *arg)
{
+ struct mdp3_dma *dma = &mdp3_res->dma[MDP3_DMA_P];
+
mdp3_res->underrun_cnt++;
- pr_err("display underrun detected count=%d\n",
+ pr_err_ratelimited("display underrun detected count=%d\n",
mdp3_res->underrun_cnt);
+ ATRACE_INT("mdp3_dma_underrun_intr_handler", mdp3_res->underrun_cnt);
+
+ if (dma->ccs_config.ccs_enable && !dma->ccs_config.ccs_dirty) {
+ dma->ccs_config.ccs_dirty = true;
+ schedule_work(&dma->underrun_work);
+ }
}
static ssize_t mdp3_show_capabilities(struct device *dev,
@@ -1826,7 +2452,7 @@ static ssize_t mdp3_show_capabilities(struct device *dev,
(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
SPRINT("mdp_version=3\n");
- SPRINT("hw_rev=%d\n", 304);
+ SPRINT("hw_rev=%d\n", 305);
SPRINT("dma_pipes=%d\n", 1);
SPRINT("\n");
@@ -1835,8 +2461,46 @@ static ssize_t mdp3_show_capabilities(struct device *dev,
static DEVICE_ATTR(caps, S_IRUGO, mdp3_show_capabilities, NULL);
+static ssize_t mdp3_store_smart_blit(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ u32 data = -1;
+ ssize_t rc = 0;
+
+ rc = kstrtoint(buf, 10, &data);
+ if (rc) {
+ pr_err("kstrtoint failed. rc=%d\n", rc);
+ return rc;
+ }
+ mdp3_res->smart_blit_en = data;
+ pr_debug("mdp3 smart blit RGB %s YUV %s\n",
+ (mdp3_res->smart_blit_en & SMART_BLIT_RGB_EN) ?
+ "ENABLED" : "DISABLED",
+ (mdp3_res->smart_blit_en & SMART_BLIT_YUV_EN) ?
+ "ENABLED" : "DISABLED");
+ return len;
+}
+
+static ssize_t mdp3_show_smart_blit(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ pr_debug("mdp3 smart blit RGB %s YUV %s\n",
+ (mdp3_res->smart_blit_en & SMART_BLIT_RGB_EN) ?
+ "ENABLED" : "DISABLED",
+ (mdp3_res->smart_blit_en & SMART_BLIT_YUV_EN) ?
+ "ENABLED" : "DISABLED");
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", mdp3_res->smart_blit_en);
+ return ret;
+}
+
+static DEVICE_ATTR(smart_blit, S_IRUGO | S_IWUSR | S_IWGRP,
+ mdp3_show_smart_blit, mdp3_store_smart_blit);
+
static struct attribute *mdp3_fs_attrs[] = {
&dev_attr_caps.attr,
+ &dev_attr_smart_blit.attr,
NULL
};
@@ -1962,24 +2626,49 @@ int mdp3_misr_set(struct mdp_misr *misr_req)
return ret;
}
+struct mdss_panel_cfg *mdp3_panel_intf_type(int intf_val)
+{
+ if (!mdp3_res || !mdp3_res->pan_cfg.init_done)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ if (mdp3_res->pan_cfg.pan_intf == intf_val)
+ return &mdp3_res->pan_cfg;
+ else
+ return NULL;
+}
+EXPORT_SYMBOL(mdp3_panel_intf_type);
+
int mdp3_footswitch_ctrl(int enable)
{
int rc = 0;
+ int active_cnt = 0;
if (!mdp3_res->fs_ena && enable) {
rc = regulator_enable(mdp3_res->fs);
if (rc) {
pr_err("mdp footswitch ctrl enable failed\n");
return -EINVAL;
- } else {
+ }
pr_debug("mdp footswitch ctrl enable success\n");
+ mdp3_enable_regulator(true);
mdp3_res->fs_ena = true;
+ } else if (!enable && mdp3_res->fs_ena) {
+ active_cnt = atomic_read(&mdp3_res->active_intf_cnt);
+ if (active_cnt != 0) {
+ /*
+ * Turning off GDSC while overlays are still
+ * active.
+ */
+ mdp3_res->idle_pc = true;
+ pr_debug("idle pc. active overlays=%d\n",
+ active_cnt);
}
- } else if (mdp3_res->fs_ena && !enable) {
+ mdp3_enable_regulator(false);
rc = regulator_disable(mdp3_res->fs);
- if (rc)
- pr_warn("mdp footswitch ctrl disable failed\n");
- else
+ if (rc) {
+ pr_err("mdp footswitch ctrl disable failed\n");
+ return -EINVAL;
+ }
mdp3_res->fs_ena = false;
} else {
pr_debug("mdp3 footswitch ctrl already configured\n");
@@ -1988,17 +2677,28 @@ int mdp3_footswitch_ctrl(int enable)
return rc;
}
-struct mdss_panel_cfg *mdp3_panel_intf_type(int intf_val)
+int mdp3_panel_get_intf_status(u32 disp_num, u32 intf_type)
{
- if (!mdp3_res || !mdp3_res->pan_cfg.init_done)
- return ERR_PTR(-EPROBE_DEFER);
+ int rc = 0, status = 0;
- if (mdp3_res->pan_cfg.pan_intf == intf_val)
- return &mdp3_res->pan_cfg;
- else
- return NULL;
+ if (intf_type != MDSS_PANEL_INTF_DSI)
+ return 0;
+
+ rc = mdp3_clk_enable(1, 0);
+ if (rc) {
+ pr_err("fail to turn on MDP core clks\n");
+ return rc;
+ }
+
+ status = (MDP3_REG_READ(MDP3_REG_DMA_P_CONFIG) & 0x180000);
+ /* DSI video mode or command mode */
+ rc = (status == 0x180000) || (status == 0x080000);
+
+ rc = mdp3_clk_enable(0, 0);
+ if (rc)
+ pr_err("fail to turn off MDP core clks\n");
+ return rc;
}
-EXPORT_SYMBOL(mdp3_panel_intf_type);
static int mdp3_probe(struct platform_device *pdev)
{
@@ -2017,6 +2717,7 @@ static int mdp3_probe(struct platform_device *pdev)
.data = NULL,
};
+ pr_debug("%s: START\n", __func__);
if (!pdev->dev.of_node) {
pr_err("MDP driver only supports device tree probe\n");
return -ENOTSUPP;
@@ -2037,6 +2738,9 @@ static int mdp3_probe(struct platform_device *pdev)
mutex_init(&mdp3_res->res_mutex);
spin_lock_init(&mdp3_res->irq_lock);
platform_set_drvdata(pdev, mdp3_res);
+ atomic_set(&mdp3_res->active_intf_cnt, 0);
+ mutex_init(&mdp3_res->reg_bus_lock);
+ INIT_LIST_HEAD(&mdp3_res->reg_bus_clist);
mdp3_res->mdss_util = mdss_get_util_intf();
if (mdp3_res->mdss_util == NULL) {
@@ -2045,12 +2749,14 @@ static int mdp3_probe(struct platform_device *pdev)
goto get_util_fail;
}
mdp3_res->mdss_util->get_iommu_domain = mdp3_get_iommu_domain;
- mdp3_res->mdss_util->iommu_attached = mdp3_iommu_is_attached;
+ mdp3_res->mdss_util->iommu_attached = is_mdss_iommu_attached;
mdp3_res->mdss_util->iommu_ctrl = mdp3_iommu_ctrl;
mdp3_res->mdss_util->bus_scale_set_quota = mdp3_bus_scale_set_quota;
mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type;
+ mdp3_res->mdss_util->dyn_clk_gating_ctrl =
+ mdp3_dynamic_clock_gating_ctrl;
+ mdp3_res->mdss_util->panel_intf_type = mdp3_panel_intf_type;
mdp3_res->mdss_util->panel_intf_status = mdp3_panel_get_intf_status;
-
rc = mdp3_parse_dt(pdev);
if (rc)
goto probe_done;
@@ -2067,24 +2773,35 @@ static int mdp3_probe(struct platform_device *pdev)
pr_err("unable to get mdss gdsc regulator\n");
return -EINVAL;
}
- rc = mdp3_footswitch_ctrl(1);
+
+ rc = mdp3_debug_init(pdev);
if (rc) {
- pr_err("unable to turn on FS\n");
+ pr_err("unable to initialize mdp debugging\n");
goto probe_done;
}
- rc = mdp3_check_version();
- if (rc) {
- pr_err("mdp3 check version failed\n");
- goto probe_done;
+ pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT_MS);
+ if (mdp3_res->idle_pc_enabled) {
+ pr_debug("%s: Enabling autosuspend\n", __func__);
+ pm_runtime_use_autosuspend(&pdev->dev);
}
+ /* Enable PM runtime */
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
- rc = mdp3_debug_init(pdev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ rc = mdp3_footswitch_ctrl(1);
+ if (rc) {
+ pr_err("unable to turn on FS\n");
+ goto probe_done;
+ }
+ }
+
+ rc = mdp3_check_version();
if (rc) {
- pr_err("unable to initialize mdp debugging\n");
+ pr_err("mdp3 check version failed\n");
goto probe_done;
}
-
rc = mdp3_register_sysfs(pdev);
if (rc)
pr_err("unable to register mdp sysfs nodes\n");
@@ -2097,7 +2814,13 @@ static int mdp3_probe(struct platform_device *pdev)
&underrun_cb);
if (rc)
pr_err("unable to configure interrupt callback\n");
+
+ rc = mdss_smmu_init(mdss_res, &pdev->dev);
+ if (rc)
+ pr_err("mdss smmu init failed\n");
+
mdp3_res->mdss_util->mdp_probe_done = true;
+ pr_debug("%s: END\n", __func__);
probe_done:
if (IS_ERR_VALUE(rc))
@@ -2134,42 +2857,118 @@ int mdp3_panel_get_boot_cfg(void)
return rc;
}
-static int mdp3_suspend_sub(struct mdp3_hw_resource *mdata)
+static int mdp3_suspend_sub(void)
{
- mdp3_enable_regulator(false);
+ mdp3_footswitch_ctrl(0);
return 0;
}
-static int mdp3_resume_sub(struct mdp3_hw_resource *mdata)
+static int mdp3_resume_sub(void)
{
- mdp3_enable_regulator(true);
+ mdp3_footswitch_ctrl(1);
return 0;
}
-static int mdp3_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int mdp3_pm_suspend(struct device *dev)
{
- struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev);
+ dev_dbg(dev, "Display pm suspend\n");
- if (!mdata)
- return -ENODEV;
+ return mdp3_suspend_sub();
+}
+
+static int mdp3_pm_resume(struct device *dev)
+{
+ dev_dbg(dev, "Display pm resume\n");
- pr_debug("display suspend\n");
+ /*
+ * It is possible that the runtime status of the mdp device may
+ * have been active when the system was suspended. Reset the runtime
+ * status to suspended state after a complete system resume.
+ */
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_enable(dev);
+
+ return mdp3_resume_sub();
+}
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP)
+static int mdp3_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ pr_debug("Display suspend\n");
- return mdp3_suspend_sub(mdata);
+ return mdp3_suspend_sub();
}
static int mdp3_resume(struct platform_device *pdev)
{
- struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev);
+ pr_debug("Display resume\n");
- if (!mdata)
- return -ENODEV;
+ return mdp3_resume_sub();
+}
+#else
+#define mdp3_suspend NULL
+#define mdp3_resume NULL
+#endif
+
+
+#ifdef CONFIG_PM_RUNTIME
+static int mdp3_runtime_resume(struct device *dev)
+{
+ bool device_on = true;
+
+ dev_dbg(dev, "Display pm runtime resume, active overlay cnt=%d\n",
+ atomic_read(&mdp3_res->active_intf_cnt));
- pr_debug("display resume\n");
+ /* do not resume panels when coming out of idle power collapse */
+ if (!mdp3_res->idle_pc)
+ device_for_each_child(dev, &device_on, mdss_fb_suspres_panel);
- return mdp3_resume_sub(mdata);
+ mdp3_footswitch_ctrl(1);
+
+ return 0;
}
+static int mdp3_runtime_idle(struct device *dev)
+{
+ dev_dbg(dev, "Display pm runtime idle\n");
+
+ return 0;
+}
+
+static int mdp3_runtime_suspend(struct device *dev)
+{
+ bool device_on = false;
+
+ dev_dbg(dev, "Display pm runtime suspend, active overlay cnt=%d\n",
+ atomic_read(&mdp3_res->active_intf_cnt));
+
+ if (mdp3_res->clk_ena) {
+ pr_debug("Clk turned on...MDP suspend failed\n");
+ return -EBUSY;
+ }
+
+ mdp3_footswitch_ctrl(0);
+
+ /* do not suspend panels when going in to idle power collapse */
+ if (!mdp3_res->idle_pc)
+ device_for_each_child(dev, &device_on, mdss_fb_suspres_panel);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops mdp3_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mdp3_pm_suspend,
+ mdp3_pm_resume)
+ SET_RUNTIME_PM_OPS(mdp3_runtime_suspend,
+ mdp3_runtime_resume,
+ mdp3_runtime_idle)
+};
+
+
static int mdp3_remove(struct platform_device *pdev)
{
struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev);
@@ -2199,6 +2998,7 @@ static struct platform_driver mdp3_driver = {
.driver = {
.name = "mdp3",
.of_match_table = mdp3_dt_match,
+ .pm = &mdp3_pm_ops,
},
};
diff --git a/drivers/video/fbdev/msm/mdp3.h b/drivers/video/fbdev/msm/mdp3.h
index 99f557321758..5a27971398b5 100644
--- a/drivers/video/fbdev/msm/mdp3.h
+++ b/drivers/video/fbdev/msm/mdp3.h
@@ -21,18 +21,28 @@
#include <linux/msm_iommu_domains.h>
+#include "mdss_dsi_clk.h"
#include "mdp3_dma.h"
#include "mdss_fb.h"
#include "mdss.h"
#define MDP_VSYNC_CLK_RATE 19200000
-#define MDP_CORE_CLK_RATE_SVS 150000000
+#define MDP_CORE_CLK_RATE_SVS 160000000
+#define MDP_CORE_CLK_RATE_SUPER_SVS 200000000
#define MDP_CORE_CLK_RATE_MAX 307200000
/* PPP cant work at SVS for panel res above qHD */
#define SVS_MAX_PIXEL (540 * 960)
#define KOFF_TIMEOUT msecs_to_jiffies(84)
+#define WAIT_DMA_TIMEOUT msecs_to_jiffies(84)
+
+/*
+ * MDP_DEINTERLACE & MDP_SHARPENING Flags are not valid for MDP3
+ * so using them together for MDP_SMART_BLIT.
+ */
+#define MDP_SMART_BLIT 0xC0000000
+
enum {
MDP3_CLK_AHB,
@@ -50,8 +60,8 @@ enum {
};
enum {
- MDP3_IOMMU_DOMAIN_SECURE,
MDP3_IOMMU_DOMAIN_UNSECURE,
+ MDP3_IOMMU_DOMAIN_SECURE,
MDP3_IOMMU_DOMAIN_MAX,
};
@@ -67,9 +77,16 @@ enum {
MDP3_CLIENT_DMA_P,
MDP3_CLIENT_DSI = 1,
MDP3_CLIENT_PPP,
+ MDP3_CLIENT_IOMMU,
MDP3_CLIENT_MAX,
};
+enum {
+ DI_PARTITION_NUM = 0,
+ DI_DOMAIN_NUM = 1,
+ DI_MAX,
+};
+
struct mdp3_bus_handle_map {
struct msm_bus_vectors *bus_vector;
struct msm_bus_paths *usecases;
@@ -100,6 +117,19 @@ struct mdp3_iommu_ctx_map {
int attached;
};
+struct mdp3_iommu_meta {
+ struct rb_node node;
+ struct ion_handle *handle;
+ struct rb_root iommu_maps;
+ struct kref ref;
+ struct sg_table *table;
+ struct dma_buf *dbuf;
+ int mapped_size;
+ unsigned long size;
+ dma_addr_t iova_addr;
+ unsigned long flags;
+};
+
#define MDP3_MAX_INTR 28
struct mdp3_intr_cb {
@@ -107,6 +137,9 @@ struct mdp3_intr_cb {
void *data;
};
+#define SMART_BLIT_RGB_EN 1
+#define SMART_BLIT_YUV_EN 2
+
struct mdp3_hw_resource {
struct platform_device *pdev;
u32 mdp_rev;
@@ -123,6 +156,9 @@ struct mdp3_hw_resource {
char __iomem *mdp_base;
size_t mdp_reg_size;
+ char __iomem *vbif_base;
+ size_t vbif_reg_size;
+
struct mdp3_bus_handle_map *bus_handle;
struct ion_client *ion_client;
@@ -136,6 +172,7 @@ struct mdp3_hw_resource {
struct mdp3_dma dma[MDP3_DMA_MAX];
struct mdp3_intf intf[MDP3_DMA_OUTPUT_SEL_MAX];
+ struct rb_root iommu_root;
spinlock_t irq_lock;
u32 irq_ref_count[MDP3_MAX_INTR];
u32 irq_mask;
@@ -157,16 +194,32 @@ struct mdp3_hw_resource {
struct regulator *vdd_cx;
struct regulator *fs;
bool fs_ena;
+ int clk_ena;
+ bool idle_pc_enabled;
+ bool idle_pc;
+ atomic_t active_intf_cnt;
+ u8 smart_blit_en;
+ bool solid_fill_vote_en;
+ struct list_head reg_bus_clist;
+ struct mutex reg_bus_lock;
};
struct mdp3_img_data {
dma_addr_t addr;
- u32 len;
+ unsigned long len;
+ u32 offset;
u32 flags;
+ u32 padding;
int p_need;
- struct file *srcp_file;
struct ion_handle *srcp_ihdl;
+ u32 dir;
+ u32 domain;
+ bool mapped;
+ bool skip_detach;
struct fd srcp_f;
+ struct dma_buf *srcp_dma_buf;
+ struct dma_buf_attachment *srcp_attachment;
+ struct sg_table *srcp_table;
};
extern struct mdp3_hw_resource *mdp3_res;
@@ -183,10 +236,11 @@ 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_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 mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data);
-int mdp3_iommu_enable(void);
-int mdp3_iommu_disable(void);
+int mdp3_put_img(struct mdp3_img_data *data, int client);
+int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data,
+ int client);
+int mdp3_iommu_enable(int client);
+int mdp3_iommu_disable(int client);
int mdp3_iommu_is_attached(void);
void mdp3_free(struct msm_fb_data_type *mfd);
int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd);
@@ -201,9 +255,20 @@ int mdp3_misr_get(struct mdp_misr *misr_resp);
void mdp3_enable_regulator(int enable);
void mdp3_check_dsi_ctrl_status(struct work_struct *work,
uint32_t interval);
+int mdp3_dynamic_clock_gating_ctrl(int enable);
int mdp3_footswitch_ctrl(int enable);
+int mdp3_qos_remapper_setup(struct mdss_panel_data *panel);
+int mdp3_splash_done(struct mdss_panel_info *panel_info);
+int mdp3_autorefresh_disable(struct mdss_panel_info *panel_info);
+u64 mdp3_clk_round_off(u64 clk_rate);
+
+void mdp3_calc_dma_res(struct mdss_panel_info *panel_info, u64 *clk_rate,
+ u64 *ab, u64 *ib, uint32_t bpp);
+
#define MDP3_REG_WRITE(addr, val) writel_relaxed(val, mdp3_res->mdp_base + addr)
#define MDP3_REG_READ(addr) readl_relaxed(mdp3_res->mdp_base + addr)
+#define VBIF_REG_WRITE(off, val) writel_relaxed(val, mdp3_res->vbif_base + off)
+#define VBIF_REG_READ(off) readl_relaxed(mdp3_res->vbif_base + off)
#endif /* MDP3_H */
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c
index a7f5bb132f94..31c0cd86df4b 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.c
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.c
@@ -21,8 +21,8 @@
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/dma-buf.h>
+#include <linux/pm_runtime.h>
-#include "mdss_dsi_clk.h"
#include "mdp3_ctrl.h"
#include "mdp3.h"
#include "mdp3_ppp.h"
@@ -39,8 +39,11 @@ static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable);
static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd);
static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd,
struct mdp_rgb_lut_data *cfg);
-static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
+static int mdp3_ctrl_lut_config(struct msm_fb_data_type *mfd,
struct mdp_rgb_lut_data *cfg);
+static void mdp3_ctrl_pp_resume(struct msm_fb_data_type *mfd);
+static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd);
+static int mdp3_ctrl_get_pack_pattern(u32 imgType);
u32 mdp_lut_inverse16[MDP_LUT_SIZE] = {
0, 65536, 32768, 21845, 16384, 13107, 10923, 9362, 8192, 7282, 6554, 5958,
@@ -79,7 +82,7 @@ static void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq)
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_put_img(data, MDP3_CLIENT_DMA_P);
}
bufq->count = 0;
bufq->push_idx = 0;
@@ -157,6 +160,9 @@ static void mdp3_dispatch_dma_done(struct work_struct *work)
static void mdp3_dispatch_clk_off(struct work_struct *work)
{
struct mdp3_session_data *session;
+ int rc;
+ bool dmap_busy;
+ int retry_count = 2;
pr_debug("%s\n", __func__);
session = container_of(work, struct mdp3_session_data,
@@ -166,12 +172,36 @@ static void mdp3_dispatch_clk_off(struct work_struct *work)
mutex_lock(&session->lock);
if (session->vsync_enabled ||
- atomic_read(&session->vsync_countdown) != 0) {
+ atomic_read(&session->vsync_countdown) != 0) {
mutex_unlock(&session->lock);
pr_debug("Ignoring clk shut down\n");
return;
}
+ if (session->intf->active) {
+retry_dma_done:
+ rc = wait_for_completion_timeout(&session->dma_completion,
+ WAIT_DMA_TIMEOUT);
+ if (rc <= 0) {
+ struct mdss_panel_data *panel;
+
+ panel = session->panel;
+ pr_debug("cmd kickoff timed out (%d)\n", rc);
+ dmap_busy = session->dma->busy();
+ if (dmap_busy) {
+ if (--retry_count) {
+ pr_err("dmap is busy, retry %d\n",
+ retry_count);
+ goto retry_dma_done;
+ }
+ pr_err("dmap is still busy, bug_on\n");
+ BUG_ON(1);
+ } else {
+ pr_debug("dmap is not busy, continue\n");
+ }
+ }
+ }
+
mdp3_ctrl_vsync_enable(session->mfd, 0);
mdp3_ctrl_clk_enable(session->mfd, 0);
mutex_unlock(&session->lock);
@@ -189,7 +219,7 @@ 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);
+ complete_all(&session->dma_completion);
}
void vsync_count_down(void *arg)
@@ -301,7 +331,7 @@ static int mdp3_ctrl_blit_req(struct msm_fb_data_type *mfd, void __user *p)
void __user *p_req;
if (copy_from_user(&(req_list_header.count), p,
- sizeof(struct mdp_blit_req_list)))
+ sizeof(struct mdp_blit_req_list)))
return -EFAULT;
p_req = p + sizeof(struct mdp_blit_req_list);
count = req_list_header.count;
@@ -328,12 +358,87 @@ 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);
+ pr_debug("fb%d vsync=%llu\n", mfd->index, vsync_ticks);
rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks);
return rc;
}
+static ssize_t mdp3_packpattern_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+ struct mdp3_session_data *mdp3_session = NULL;
+ int rc;
+ u32 pattern = 0;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EAGAIN;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+
+ pattern = mdp3_session->dma->output_config.pack_pattern;
+
+ /* If pattern was found to be 0 then get pattern for fb imagetype */
+ if (!pattern)
+ pattern = mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
+
+ pr_debug("fb%d pack_pattern c= %d.", mfd->index, pattern);
+ rc = scnprintf(buf, PAGE_SIZE, "packpattern=%d\n", pattern);
+ return rc;
+}
+
+static ssize_t mdp3_dyn_pu_show(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 mdp3_session_data *mdp3_session = NULL;
+ int ret, state;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EAGAIN;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ state = (mdp3_session->dyn_pu_state >= 0) ?
+ mdp3_session->dyn_pu_state : -1;
+ ret = scnprintf(buf, PAGE_SIZE, "%d", state);
+ return ret;
+}
+
+static ssize_t mdp3_dyn_pu_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = fbi->par;
+ struct mdp3_session_data *mdp3_session = NULL;
+ int ret, dyn_pu;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EAGAIN;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ ret = kstrtoint(buf, 10, &dyn_pu);
+ if (ret) {
+ pr_err("Invalid input for partial update: ret = %d\n", ret);
+ return ret;
+ }
+
+ mdp3_session->dyn_pu_state = dyn_pu;
+ sysfs_notify(&dev->kobj, NULL, "dyn_pu");
+ return count;
+}
+
static DEVICE_ATTR(vsync_event, S_IRUGO, mdp3_vsync_show_event, NULL);
+static DEVICE_ATTR(packpattern, S_IRUGO, mdp3_packpattern_show, NULL);
+static DEVICE_ATTR(dyn_pu, S_IRUGO | S_IWUSR | S_IWGRP, mdp3_dyn_pu_show,
+ mdp3_dyn_pu_store);
+
+static struct attribute *generic_attrs[] = {
+ &dev_attr_packpattern.attr,
+ &dev_attr_dyn_pu.attr,
+ NULL,
+};
static struct attribute *vsync_fs_attrs[] = {
&dev_attr_vsync_event.attr,
@@ -344,6 +449,10 @@ static struct attribute_group vsync_fs_attr_group = {
.attrs = vsync_fs_attrs,
};
+static struct attribute_group generic_attr_group = {
+ .attrs = generic_attrs,
+};
+
static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable)
{
struct mdp3_session_data *session;
@@ -377,13 +486,12 @@ static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable)
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;
u64 ab = 0;
u64 ib = 0;
- ab = panel_info->xres * panel_info->yres * 4 * 2;
- ab *= panel_info->mipi.frame_rate;
- ib = (ab * 3) / 2;
+ mdp3_calc_dma_res(mfd->panel_info, NULL, &ab, &ib,
+ ppp_bpp(mfd->fb_imgType));
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
} else {
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
@@ -395,8 +503,12 @@ static int mdp3_ctrl_res_req_clk(struct msm_fb_data_type *mfd, int status)
{
int rc = 0;
if (status) {
+ u64 mdp_clk_rate = 0;
- mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, MDP_CORE_CLK_RATE_SVS,
+ mdp3_calc_dma_res(mfd->panel_info, &mdp_clk_rate,
+ NULL, NULL, 0);
+
+ mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, mdp_clk_rate,
MDP3_CLIENT_DMA_P);
mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE,
MDP3_CLIENT_DMA_P);
@@ -456,7 +568,7 @@ static int mdp3_ctrl_get_source_format(u32 imgType)
static int mdp3_ctrl_get_pack_pattern(u32 imgType)
{
int packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_RGB;
- if (imgType == MDP_RGBA_8888)
+ if (imgType == MDP_RGBA_8888 || imgType == MDP_RGB_888)
packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_BGR;
return packPattern;
}
@@ -464,7 +576,7 @@ static int mdp3_ctrl_get_pack_pattern(u32 imgType)
static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd,
struct mdp3_intf *intf)
{
- int rc;
+ int rc = 0;
struct mdp3_intf_cfg cfg;
struct mdp3_video_intf_cfg *video = &cfg.video;
struct mdss_panel_info *p = mfd->panel_info;
@@ -479,6 +591,9 @@ static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd,
int v_pulse_width = p->lcdc.v_pulse_width;
int hsync_period = h_front_porch + h_back_porch + w + h_pulse_width;
int vsync_period = v_front_porch + v_back_porch + h + v_pulse_width;
+ struct mdp3_session_data *mdp3_session;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
vsync_period *= hsync_period;
cfg.type = mdp3_ctrl_get_intf_type(mfd);
@@ -512,10 +627,12 @@ static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd,
} else
return -EINVAL;
- if (intf->config)
- rc = intf->config(intf, &cfg);
- else
- rc = -EINVAL;
+ if (!(mdp3_session->in_splash_screen)) {
+ if (intf->config)
+ rc = intf->config(intf, &cfg);
+ else
+ rc = -EINVAL;
+ }
return rc;
}
@@ -534,6 +651,9 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
int vtotal, vporch;
struct mdp3_notification dma_done_callback;
struct mdp3_tear_check te;
+ struct mdp3_session_data *mdp3_session;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
vbp = panel_info->lcdc.v_back_porch;
vfp = panel_info->lcdc.v_front_porch;
@@ -544,12 +664,10 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
fix = &fbi->fix;
var = &fbi->var;
- sourceConfig.format = mdp3_ctrl_get_source_format(mfd->fb_imgType);
sourceConfig.width = panel_info->xres;
sourceConfig.height = panel_info->yres;
sourceConfig.x = 0;
sourceConfig.y = 0;
- sourceConfig.stride = fix->line_length;
sourceConfig.buf = mfd->iova;
sourceConfig.vporch = vporch;
sourceConfig.vsync_count =
@@ -559,12 +677,24 @@ 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->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;
+ if (dma->update_src_cfg) {
+ /* configuration has been updated through PREPARE call */
+ sourceConfig.format = dma->source_config.format;
+ sourceConfig.stride = dma->source_config.stride;
+ outputConfig.pack_pattern = dma->output_config.pack_pattern;
+ } else {
+ sourceConfig.format =
+ mdp3_ctrl_get_source_format(mfd->fb_imgType);
+ outputConfig.pack_pattern =
+ mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
+ sourceConfig.stride = fix->line_length;
+ }
+
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;
@@ -576,10 +706,19 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
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
+ if (dma->dma_config) {
+ if (!panel_info->partial_update_enabled) {
+ dma->roi.w = sourceConfig.width;
+ dma->roi.h = sourceConfig.height;
+ dma->roi.x = sourceConfig.x;
+ dma->roi.y = sourceConfig.y;
+ }
+ rc = dma->dma_config(dma, &sourceConfig, &outputConfig,
+ mdp3_session->in_splash_screen);
+ } else {
+ pr_err("%s: dma config failed\n", __func__);
rc = -EINVAL;
+ }
if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
if (dma->dma_sync_config)
@@ -609,22 +748,50 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
return -ENODEV;
}
mutex_lock(&mdp3_session->lock);
+
+ panel = mdp3_session->panel;
+ pr_err("%s %d in_splash_screen %d\n", __func__, __LINE__,
+ mdp3_session->in_splash_screen);
+ /* make sure DSI host is initialized properly */
+ if (panel) {
+ pr_debug("%s : dsi host init, power state = %d Splash %d\n",
+ __func__, mfd->panel_power_state,
+ mdp3_session->in_splash_screen);
+ if (mdss_fb_is_power_on_lp(mfd) ||
+ mdp3_session->in_splash_screen) {
+ /* Turn on panel so that it can exit low power mode */
+ pr_err("%s %d\n", __func__, __LINE__);
+ mdp3_clk_enable(1, 0);
+ rc = panel->event_handler(panel,
+ MDSS_EVENT_LINK_READY, NULL);
+ rc |= panel->event_handler(panel,
+ MDSS_EVENT_UNBLANK, NULL);
+ rc |= panel->event_handler(panel,
+ MDSS_EVENT_PANEL_ON, NULL);
+ mdp3_clk_enable(0, 0);
+ }
+ }
+
if (mdp3_session->status) {
- pr_debug("fb%d is on already", mfd->index);
- goto on_error;
+ pr_err("fb%d is on already\n", mfd->index);
+ goto end;
}
if (mdp3_session->intf->active) {
pr_debug("continuous splash screen, initialized already\n");
- goto on_error;
+ mdp3_session->status = 1;
+ goto end;
}
- mdp3_enable_regulator(true);
- rc = mdp3_footswitch_ctrl(1);
- if (rc) {
- pr_err("fail to enable mdp footswitch ctrl\n");
- goto on_error;
- }
+ /*
+ * Get a reference to the runtime pm device.
+ * If idle pc feature is enabled, it will be released
+ * at end of this routine else, when device is turned off.
+ */
+ pm_runtime_get_sync(&mdp3_res->pdev->dev);
+
+ /* Increment the overlay active count */
+ atomic_inc(&mdp3_res->active_intf_cnt);
mdp3_ctrl_notifier_register(mdp3_session,
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
@@ -635,19 +802,35 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
goto on_error;
}
- panel = mdp3_session->panel;
+ rc = mdp3_dynamic_clock_gating_ctrl(0);
+ if (rc) {
+ pr_err("fail to disable dynamic clock gating\n");
+ goto on_error;
+ }
+ mdp3_qos_remapper_setup(panel);
+
+ rc = mdp3_ctrl_res_req_clk(mfd, 1);
+ if (rc) {
+ pr_err("fail to request mdp clk resource\n");
+ goto on_error;
+ }
+
if (panel->event_handler) {
rc = panel->event_handler(panel, MDSS_EVENT_LINK_READY, NULL);
rc |= panel->event_handler(panel, MDSS_EVENT_UNBLANK, NULL);
rc |= panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
+ if (panel->panel_info.type == MIPI_CMD_PANEL) {
+ struct dsi_panel_clk_ctrl clk_ctrl;
+
+ clk_ctrl.state = MDSS_DSI_CLK_ON;
+ clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT;
+ rc |= panel->event_handler(panel,
+ MDSS_EVENT_PANEL_CLK_CTRL,
+ (void *)&clk_ctrl);
}
- if (rc) {
- pr_err("fail to turn on the panel\n");
- goto on_error;
}
- rc = mdp3_ctrl_res_req_clk(mfd, 1);
if (rc) {
- pr_err("fail to request mdp clk resource\n");
+ pr_err("fail to turn on the panel\n");
goto on_error;
}
@@ -671,10 +854,22 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
mdp3_session->clk_on = 1;
mdp3_session->first_commit = true;
+ if (mfd->panel_info->panel_dead)
+ mdp3_session->esd_recovery = true;
-on_error:
- if (!rc)
mdp3_session->status = 1;
+
+ mdp3_ctrl_pp_resume(mfd);
+on_error:
+ if (rc || (mdp3_res->idle_pc_enabled &&
+ (mfd->panel_info->type == MIPI_CMD_PANEL))) {
+ if (rc) {
+ pr_err("Failed to turn on fb%d\n", mfd->index);
+ atomic_dec(&mdp3_res->active_intf_cnt);
+ }
+ pm_runtime_put(&mdp3_res->pdev->dev);
+ }
+end:
mutex_unlock(&mdp3_session->lock);
return rc;
}
@@ -682,6 +877,7 @@ on_error:
static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
{
int rc = 0;
+ bool intf_stopped = true;
struct mdp3_session_data *mdp3_session;
struct mdss_panel_data *panel;
@@ -693,116 +889,135 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
return -ENODEV;
}
+ /*
+ * Keep a reference to the runtime pm until the overlay is turned
+ * off, and then release this last reference at the end. This will
+ * help in distinguishing between idle power collapse versus suspend
+ * power collapse
+ */
+ pm_runtime_get_sync(&mdp3_res->pdev->dev);
+
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;
+ pr_debug("Requested power state = %d\n", mfd->panel_power_state);
+ if (mdss_fb_is_power_on_lp(mfd)) {
+ /*
+ * Transition to low power
+ * As display updates are expected in low power mode,
+ * keep the interface and clocks on.
+ */
+ intf_stopped = false;
+ } else {
+ /* Transition to display off */
+ if (!mdp3_session->status) {
+ pr_debug("fb%d is off already", mfd->index);
+ goto off_error;
+ }
+ if (panel && panel->set_backlight)
+ panel->set_backlight(panel, 0);
}
- mdp3_ctrl_clk_enable(mfd, 1);
-
- mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P);
-
+ /*
+ * While transitioning from interactive to low power,
+ * events need to be sent to the interface so that the
+ * panel can be configured in low power mode
+ */
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);
+ rc = panel->event_handler(panel, MDSS_EVENT_BLANK,
+ (void *) (long int)mfd->panel_power_state);
if (rc)
- pr_debug("fail to stop the MDP3 dma\n");
- /* Wait for TG to turn off */
- msleep(20);
-
- mdp3_irq_deregister();
-
- pr_debug("mdp3_ctrl_off stop clock\n");
- if (mdp3_session->clk_on) {
- rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
+ pr_err("EVENT_BLANK error (%d)\n", rc);
+
+ if (intf_stopped) {
+ if (!mdp3_session->clk_on)
+ mdp3_ctrl_clk_enable(mfd, 1);
+ /* PP related programming for ctrl off */
+ mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P);
+ mutex_lock(&mdp3_session->dma->pp_lock);
+ mdp3_session->dma->ccs_config.ccs_dirty = false;
+ mdp3_session->dma->lut_config.lut_dirty = false;
+ mutex_unlock(&mdp3_session->dma->pp_lock);
+
+ rc = mdp3_session->dma->stop(mdp3_session->dma,
+ mdp3_session->intf);
if (rc)
- pr_err("mdp clock resource release failed\n");
-
- pr_debug("mdp3_ctrl_off stop dsi controller\n");
- if (panel->event_handler)
- rc = panel->event_handler(panel,
- MDSS_EVENT_BLANK, NULL);
- if (rc)
- pr_err("fail to turn off the panel\n");
- }
-
- mdp3_ctrl_notifier_unregister(mdp3_session,
- &mdp3_session->mfd->mdp_sync_pt_data.notifier);
- mdp3_enable_regulator(false);
- mdp3_footswitch_ctrl(0);
- 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);
- }
- mutex_unlock(&mdp3_session->lock);
- return 0;
-}
+ pr_debug("fail to stop the MDP3 dma\n");
+ /* Wait to ensure TG to turn off */
+ msleep(20);
+ mfd->panel_info->cont_splash_enabled = 0;
-static int mdp3_ctrl_reset_cmd(struct msm_fb_data_type *mfd)
-{
- int rc = 0;
- struct mdp3_session_data *mdp3_session;
- struct mdp3_dma *mdp3_dma;
- struct mdss_panel_data *panel;
- struct mdp3_notification vsync_client;
+ /* Disable Auto refresh once continuous splash disabled */
+ mdp3_autorefresh_disable(mfd->panel_info);
+ mdp3_splash_done(mfd->panel_info);
- pr_debug("mdp3_ctrl_reset_cmd\n");
- mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
- if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
- !mdp3_session->intf) {
- pr_err("mdp3_ctrl_reset no device");
- return -ENODEV;
+ mdp3_irq_deregister();
}
- panel = mdp3_session->panel;
- mdp3_dma = mdp3_session->dma;
- mutex_lock(&mdp3_session->lock);
+ if (panel->event_handler)
+ rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF,
+ (void *) (long int)mfd->panel_power_state);
+ if (rc)
+ pr_err("EVENT_PANEL_OFF error (%d)\n", rc);
+
+ if (intf_stopped) {
+ if (mdp3_session->clk_on) {
+ pr_debug("mdp3_ctrl_off stop clock\n");
+ if (panel->event_handler &&
+ (panel->panel_info.type == MIPI_CMD_PANEL)) {
+ struct dsi_panel_clk_ctrl clk_ctrl;
+
+ clk_ctrl.state = MDSS_DSI_CLK_OFF;
+ clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT;
+ rc |= panel->event_handler(panel,
+ MDSS_EVENT_PANEL_CLK_CTRL,
+ (void *)&clk_ctrl);
+ }
- vsync_client = mdp3_dma->vsync_client;
+ rc = mdp3_dynamic_clock_gating_ctrl(1);
+ rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
+ if (rc)
+ pr_err("mdp clock resource release failed\n");
+ }
- rc = mdp3_dma->stop(mdp3_dma, mdp3_session->intf);
- if (rc) {
- pr_err("fail to stop the MDP3 dma\n");
- goto reset_error;
- }
+ mdp3_ctrl_notifier_unregister(mdp3_session,
+ &mdp3_session->mfd->mdp_sync_pt_data.notifier);
- rc = mdp3_iommu_enable();
- if (rc) {
- pr_err("fail to attach dma iommu\n");
- goto reset_error;
+ 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;
+ mdp3_res->solid_fill_vote_en = false;
+ mdp3_session->status = 0;
+ if (atomic_dec_return(&mdp3_res->active_intf_cnt) != 0) {
+ pr_warn("active_intf_cnt unbalanced\n");
+ atomic_set(&mdp3_res->active_intf_cnt, 0);
+ }
+ /*
+ * Release the pm runtime reference held when
+ * idle pc feature is not enabled
+ */
+ if (!mdp3_res->idle_pc_enabled ||
+ (mfd->panel_info->type != MIPI_CMD_PANEL)) {
+ rc = pm_runtime_put(&mdp3_res->pdev->dev);
+ if (rc)
+ pr_err("%s: pm_runtime_put failed (rc %d)\n",
+ __func__, rc);
+ }
+ 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);
+ }
}
-
- mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
- mdp3_ctrl_dma_init(mfd, mdp3_dma);
-
- if (vsync_client.handler)
- mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
-
- mdp3_session->first_commit = true;
- mdp3_session->in_splash_screen = 0;
-
-reset_error:
+off_error:
mutex_unlock(&mdp3_session->lock);
- return rc;
-}
+ /* Release the last reference to the runtime device */
+ pm_runtime_put(&mdp3_res->pdev->dev);
+ return 0;
+}
static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd)
{
@@ -820,73 +1035,44 @@ static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd)
return -ENODEV;
}
- if (mfd->panel.type == MIPI_CMD_PANEL) {
- rc = mdp3_ctrl_reset_cmd(mfd);
- return rc;
- }
-
panel = mdp3_session->panel;
mdp3_dma = mdp3_session->dma;
mutex_lock(&mdp3_session->lock);
-
- vsync_client = mdp3_dma->vsync_client;
- 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 %d\n", rc);
- goto reset_error;
- }
-
- rc = mdp3_put_mdp_dsi_clk();
- if (rc) {
- pr_err("fail to release mdp clocks\n");
- goto reset_error;
- }
-
- rc = panel->event_handler(panel, MDSS_EVENT_BLANK, NULL);
- if (rc) {
- pr_err("fail to blank the panel\n");
- goto reset_error;
+ if (mdp3_res->idle_pc) {
+ mdp3_clk_enable(1, 0);
+ mdp3_dynamic_clock_gating_ctrl(0);
+ mdp3_qos_remapper_setup(panel);
}
- rc = mdp3_iommu_enable();
+ rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
if (rc) {
pr_err("fail to attach dma iommu\n");
+ if (mdp3_res->idle_pc)
+ mdp3_clk_enable(0, 0);
goto reset_error;
}
- rc = panel->event_handler(panel, MDSS_EVENT_UNBLANK, NULL);
- if (rc) {
- pr_err("fail to unblank the panel\n");
- goto reset_error;
- }
-
- rc = panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
- if (rc) {
- pr_err("fail to turn on the panel\n");
- goto reset_error;
- }
-
- rc = mdp3_get_mdp_dsi_clk();
- if (rc) {
- pr_err("fail to turn on mdp clks\n");
- goto reset_error;
- }
+ vsync_client = mdp3_dma->vsync_client;
mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
mdp3_ctrl_dma_init(mfd, mdp3_dma);
-
+ mdp3_ppp_init();
+ mdp3_ctrl_pp_resume(mfd);
if (vsync_client.handler)
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
- mdp3_session->first_commit = true;
+ if (!mdp3_res->idle_pc) {
+ mdp3_session->first_commit = true;
+ mfd->panel_info->cont_splash_enabled = 0;
mdp3_session->in_splash_screen = 0;
+ mdp3_splash_done(mfd->panel_info);
+ /* Disable Auto refresh */
+ mdp3_autorefresh_disable(mfd->panel_info);
+ } else {
+ mdp3_res->idle_pc = false;
+ mdp3_clk_enable(0, 0);
+ mdp3_iommu_disable(MDP3_CLIENT_DMA_P);
+ }
reset_error:
mutex_unlock(&mdp3_session->lock);
@@ -926,13 +1112,15 @@ static int mdp3_overlay_set(struct msm_fb_data_type *mfd,
stride = req->src.width * ppp_bpp(req->src.format);
format = mdp3_ctrl_get_source_format(req->src.format);
- mutex_lock(&mdp3_session->lock);
if (mdp3_session->overlay.id != req->id)
pr_err("overlay was not released, continue to recover\n");
-
- mdp3_session->overlay = *req;
+ /*
+ * A change in overlay structure will always come with
+ * MSMFB_NEW_REQUEST for MDP3
+ */
if (req->id == MSMFB_NEW_REQUEST) {
+ mutex_lock(&mdp3_session->lock);
if (dma->source_config.stride != stride ||
dma->source_config.format != format) {
dma->source_config.format = format;
@@ -941,11 +1129,11 @@ static int mdp3_overlay_set(struct msm_fb_data_type *mfd,
mdp3_ctrl_get_pack_pattern(req->src.format);
dma->update_src_cfg = true;
}
+ mdp3_session->overlay = *req;
mdp3_session->overlay.id = 1;
req->id = 1;
- }
-
mutex_unlock(&mdp3_session->lock);
+ }
return rc;
}
@@ -983,22 +1171,25 @@ static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd,
struct mdp3_img_data data;
struct mdp3_dma *dma = mdp3_session->dma;
- rc = mdp3_get_img(img, &data);
+ memset(&data, 0, sizeof(struct mdp3_img_data));
+ rc = mdp3_get_img(img, &data, MDP3_CLIENT_DMA_P);
if (rc) {
pr_err("fail to get overlay buffer\n");
return rc;
}
if (data.len < dma->source_config.stride * dma->source_config.height) {
- pr_err("buf length is smaller than required by dma configuration\n");
- mdp3_put_img(&data);
+ pr_err("buf size(0x%lx) is smaller than dma config(0x%x)\n",
+ data.len, (dma->source_config.stride *
+ dma->source_config.height));
+ mdp3_put_img(&data, MDP3_CLIENT_DMA_P);
return -EINVAL;
}
rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data);
if (rc) {
pr_err("fail to queue the overlay buffer, buffer drop\n");
- mdp3_put_img(&data);
+ mdp3_put_img(&data, MDP3_CLIENT_DMA_P);
return rc;
}
return 0;
@@ -1039,10 +1230,11 @@ bool update_roi(struct mdp3_rect oldROI, struct mdp_rect newROI)
bool is_roi_valid(struct mdp3_dma_source source_config, struct mdp_rect roi)
{
- return ((roi.x >= source_config.x) &&
+ return (roi.w > 0) && (roi.h > 0) &&
+ (roi.x >= source_config.x) &&
((roi.x + roi.w) <= source_config.width) &&
(roi.y >= source_config.y) &&
- ((roi.y + roi.h) <= source_config.height));
+ ((roi.y + roi.h) <= source_config.height);
}
static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
@@ -1052,7 +1244,7 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
struct mdp3_img_data *data;
struct mdss_panel_info *panel_info;
int rc = 0;
- bool reset_done = false;
+ static bool splash_done;
struct mdss_panel_data *panel;
if (!mfd || !mfd->mdp.private1)
@@ -1067,6 +1259,7 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
pr_debug("no buffer in queue yet\n");
return -EPERM;
}
+
if (panel_info->partial_update_enabled &&
is_roi_valid(mdp3_session->dma->source_config, cmt_data->l_roi)
&& update_roi(mdp3_session->dma->roi, cmt_data->l_roi)) {
@@ -1075,17 +1268,23 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
mdp3_session->dma->roi.w = cmt_data->l_roi.w;
mdp3_session->dma->roi.h = cmt_data->l_roi.h;
mdp3_session->dma->update_src_cfg = true;
+ pr_debug("%s: ROI: x=%d y=%d w=%d h=%d\n", __func__,
+ mdp3_session->dma->roi.x,
+ mdp3_session->dma->roi.y,
+ mdp3_session->dma->roi.w,
+ mdp3_session->dma->roi.h);
}
panel = mdp3_session->panel;
- if (mdp3_session->in_splash_screen) {
- pr_debug("continuous splash screen, IOMMU not attached\n");
+ if (mdp3_session->in_splash_screen ||
+ mdp3_res->idle_pc) {
+ pr_debug("%s: reset- in_splash = %d, idle_pc = %d", __func__,
+ mdp3_session->in_splash_screen, mdp3_res->idle_pc);
rc = mdp3_ctrl_reset(mfd);
if (rc) {
pr_err("fail to reset display\n");
return -EINVAL;
}
- reset_done = true;
}
mutex_lock(&mdp3_session->lock);
@@ -1136,18 +1335,33 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
mdp3_release_splash_memory(mfd);
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
if (data)
- mdp3_put_img(data);
+ mdp3_put_img(data, MDP3_CLIENT_DMA_P);
}
if (mdp3_session->first_commit) {
- /*wait for one frame time to ensure frame is sent to panel*/
- msleep(1000 / panel_info->mipi.frame_rate);
+ /*wait to ensure frame is sent to panel*/
+ if (panel_info->mipi.init_delay)
+ msleep(((1000 / panel_info->mipi.frame_rate) + 1) *
+ panel_info->mipi.init_delay);
+ else
+ msleep(1000 / panel_info->mipi.frame_rate);
mdp3_session->first_commit = false;
+ if (panel)
+ rc |= panel->event_handler(panel,
+ MDSS_EVENT_POST_PANEL_ON, NULL);
}
mdp3_session->vsync_before_commit = 0;
- if (reset_done && (panel && panel->set_backlight))
- panel->set_backlight(panel, panel->panel_info.bl_max);
+ if (!splash_done || mdp3_session->esd_recovery == true) {
+ if (panel && panel->set_backlight)
+ panel->set_backlight(panel, panel->panel_info.bl_max);
+ splash_done = true;
+ mdp3_session->esd_recovery = false;
+ }
+
+ /* start vsync tick countdown for cmd mode if vsync isn't enabled */
+ if (mfd->panel.type == MIPI_CMD_PANEL && !mdp3_session->vsync_enabled)
+ mdp3_ctrl_vsync_enable(mdp3_session->mfd, 0);
mutex_unlock(&mdp3_session->lock);
@@ -1204,8 +1418,10 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
if (!mdp3_session || !mdp3_session->dma)
return;
- if (mdp3_session->in_splash_screen) {
- pr_debug("continuous splash screen, IOMMU not attached\n");
+ if (mdp3_session->in_splash_screen ||
+ mdp3_res->idle_pc) {
+ pr_debug("%s: reset- in_splash = %d, idle_pc = %d", __func__,
+ mdp3_session->in_splash_screen, mdp3_res->idle_pc);
rc = mdp3_ctrl_reset(mfd);
if (rc) {
pr_err("fail to reset display\n");
@@ -1250,7 +1466,7 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
MDP_NOTIFY_FRAME_TIMEOUT);
} else {
if (mdp3_ctrl_get_intf_type(mfd) ==
- MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
mdp3_ctrl_notify(mdp3_session,
MDP_NOTIFY_FRAME_DONE);
}
@@ -1265,17 +1481,26 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
mdp3_clk_enable(0, 0);
}
+ panel = mdp3_session->panel;
if (mdp3_session->first_commit) {
- /*wait for one frame time to ensure frame is sent to panel*/
- msleep(1000 / panel_info->mipi.frame_rate);
+ /*wait to ensure frame is sent to panel*/
+ if (panel_info->mipi.init_delay)
+ msleep(((1000 / panel_info->mipi.frame_rate) + 1) *
+ panel_info->mipi.init_delay);
+ else
+ msleep(1000 / panel_info->mipi.frame_rate);
mdp3_session->first_commit = false;
+ if (panel)
+ panel->event_handler(panel, MDSS_EVENT_POST_PANEL_ON,
+ NULL);
}
mdp3_session->vsync_before_commit = 0;
- panel = mdp3_session->panel;
- if (!splash_done && (panel && panel->set_backlight)) {
- panel->set_backlight(panel, panel->panel_info.bl_max);
+ if (!splash_done || mdp3_session->esd_recovery == true) {
+ if (panel && panel->set_backlight)
+ panel->set_backlight(panel, panel->panel_info.bl_max);
splash_done = true;
+ mdp3_session->esd_recovery = false;
}
@@ -1319,7 +1544,7 @@ static int mdp3_get_metadata(struct msm_fb_data_type *mfd,
mfd->panel_info->mipi.frame_rate;
break;
case metadata_op_get_caps:
- metadata->data.caps.mdp_rev = 304;
+ metadata->data.caps.mdp_rev = 305;
metadata->data.caps.rgb_pipes = 0;
metadata->data.caps.vig_pipes = 0;
metadata->data.caps.dma_pipes = 1;
@@ -1388,10 +1613,17 @@ int mdp3_validate_scale_config(struct mdp_bl_scale_data *data)
int mdp3_validate_csc_data(struct mdp_csc_cfg_data *data)
{
int i;
+ bool mv_valid = false;
for (i = 0; i < 9; i++) {
if (data->csc_data.csc_mv[i] >=
MDP_HISTOGRAM_CSC_MATRIX_MAX)
return -EINVAL;
+ if ((!mv_valid) && (data->csc_data.csc_mv[i] != 0))
+ mv_valid = true;
+ }
+ if (!mv_valid) {
+ pr_err("%s: black screen data! csc_mv is all 0s\n", __func__);
+ return -EINVAL;
}
for (i = 0; i < 3; i++) {
if (data->csc_data.csc_pre_bv[i] >=
@@ -1418,6 +1650,12 @@ static int mdp3_histogram_start(struct mdp3_session_data *session,
int ret;
struct mdp3_dma_histogram_config histo_config;
+ mutex_lock(&session->lock);
+ if (!session->status) {
+ mutex_unlock(&session->lock);
+ return -EPERM;
+ }
+
pr_debug("mdp3_histogram_start\n");
ret = mdp3_validate_start_req(req);
@@ -1433,9 +1671,9 @@ static int mdp3_histogram_start(struct mdp3_session_data *session,
mutex_lock(&session->histo_lock);
if (session->histo_status) {
- pr_err("mdp3_histogram_start already started\n");
+ pr_info("mdp3_histogram_start already started\n");
mutex_unlock(&session->histo_lock);
- return -EBUSY;
+ return 0;
}
mdp3_res_update(1, 0, MDP3_CLIENT_DMA_P);
@@ -1466,6 +1704,7 @@ static int mdp3_histogram_start(struct mdp3_session_data *session,
histogram_start_err:
mdp3_res_update(0, 0, MDP3_CLIENT_DMA_P);
mutex_unlock(&session->histo_lock);
+ mutex_unlock(&session->lock);
return ret;
}
@@ -1483,6 +1722,7 @@ static int mdp3_histogram_stop(struct mdp3_session_data *session,
mutex_lock(&session->histo_lock);
if (!session->histo_status) {
+ pr_debug("mdp3_histogram_stop already stopped!");
ret = 0;
goto histogram_stop_err;
}
@@ -1512,21 +1752,21 @@ static int mdp3_histogram_collect(struct mdp3_session_data *session,
return -EINVAL;
}
- if (!session->clk_on) {
- pr_debug("mdp/dsi clock off currently\n");
- return -EPERM;
- }
-
mutex_lock(&session->histo_lock);
if (!session->histo_status) {
- pr_err("mdp3_histogram_collect not started\n");
+ pr_debug("mdp3_histogram_collect not started\n");
mutex_unlock(&session->histo_lock);
- return -EPERM;
+ return -EPROTO;
}
mutex_unlock(&session->histo_lock);
+ if (!session->clk_on) {
+ pr_debug("mdp/dsi clock off currently\n");
+ return -EPERM;
+ }
+
mdp3_clk_enable(1, 0);
ret = session->dma->get_histo(session->dma);
mdp3_clk_enable(0, 0);
@@ -1594,14 +1834,16 @@ static int mdp3_csc_config(struct mdp3_session_data *session,
return -EINVAL;
}
- session->cc_vect_sel = (session->cc_vect_sel + 1) % 2;
+ mutex_lock(&session->lock);
+ mutex_lock(&session->dma->pp_lock);
+ session->dma->cc_vect_sel = (session->dma->cc_vect_sel + 1) % 2;
config.ccs_enable = 1;
- config.ccs_sel = session->cc_vect_sel;
- config.pre_limit_sel = session->cc_vect_sel;
- config.post_limit_sel = session->cc_vect_sel;
- config.pre_bias_sel = session->cc_vect_sel;
- config.post_bias_sel = session->cc_vect_sel;
+ config.ccs_sel = session->dma->cc_vect_sel;
+ config.pre_limit_sel = session->dma->cc_vect_sel;
+ config.post_limit_sel = session->dma->cc_vect_sel;
+ config.pre_bias_sel = session->dma->cc_vect_sel;
+ config.post_bias_sel = session->dma->cc_vect_sel;
config.ccs_dirty = true;
ccs.mv = data->csc_data.csc_mv;
@@ -1610,10 +1852,13 @@ static int mdp3_csc_config(struct mdp3_session_data *session,
ccs.pre_lv = data->csc_data.csc_pre_lv;
ccs.post_lv = data->csc_data.csc_post_lv;
- mutex_lock(&session->lock);
+ /* cache one copy of setting for suspend/resume reconfiguring */
+ session->dma->ccs_cache = *data;
+
mdp3_clk_enable(1, 0);
ret = session->dma->config_ccs(session->dma, &config, &ccs);
mdp3_clk_enable(0, 0);
+ mutex_unlock(&session->dma->pp_lock);
mutex_unlock(&session->lock);
return ret;
}
@@ -1646,6 +1891,11 @@ static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd,
&mdp_pp.data.bl_scale_data);
break;
case mdp_op_csc_cfg:
+ /* Checking state of dyn_pu before programming CSC block */
+ if (mdp3_session->dyn_pu_state) {
+ pr_debug("Partial update feature is enabled.\n");
+ return -EPERM;
+ }
ret = mdp3_validate_csc_data(&(mdp_pp.data.csc_cfg_data));
if (ret) {
pr_err("%s: invalid csc data\n", __func__);
@@ -1664,10 +1914,12 @@ static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd,
ret = mdp3_ctrl_lut_read(mfd,
&(lut->data.rgb_lut_data));
else
- ret = mdp3_ctrl_lut_update(mfd,
+ ret = mdp3_ctrl_lut_config(mfd,
&(lut->data.rgb_lut_data));
if (ret)
- pr_err("%s: invalid rgb lut data\n", __func__);
+ pr_err("RGB LUT ioctl failed\n");
+ else
+ ret = copy_to_user(argp, &mdp_pp, sizeof(mdp_pp));
break;
default:
@@ -1746,6 +1998,23 @@ static int mdp3_validate_lut_data(struct fb_cmap *cmap)
return 0;
}
+static inline int mdp3_copy_lut_buffer(struct fb_cmap *dst, struct fb_cmap *src)
+{
+ if (!dst || !src || !dst->red || !dst->blue || !dst->green ||
+ !src->red || !src->green || !src->blue) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ dst->start = src->start;
+ dst->len = src->len;
+
+ memcpy(dst->red, src->red, MDP_LUT_SIZE * sizeof(u16));
+ memcpy(dst->green, src->green, MDP_LUT_SIZE * sizeof(u16));
+ memcpy(dst->blue, src->blue, MDP_LUT_SIZE * sizeof(u16));
+ return 0;
+}
+
static int mdp3_alloc_lut_buffer(struct platform_device *pdev, void **cmap)
{
struct fb_cmap *map;
@@ -1809,7 +2078,7 @@ static void mdp3_free_lut_buffer(struct platform_device *pdev, void **cmap)
map = NULL;
}
-static void mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma)
+static int mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma)
{
int i = 0;
u32 r = 0, g = 0, b = 0;
@@ -1819,7 +2088,7 @@ static void mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma)
!dma->gc_cmap->blue || !dma->hist_cmap->red ||
!dma->hist_cmap->green || !dma->hist_cmap->blue) {
pr_err("Invalid params\n");
- return;
+ return -EINVAL;
}
for (i = 1; i < MDP_LUT_SIZE; i++) {
@@ -1834,17 +2103,17 @@ static void mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma)
cmap->green[i] = (g >> 16) & 0xFF;
cmap->blue[i] = (b >> 16) & 0xFF;
}
+ return 0;
}
+/* Called from within pp_lock and session lock locked context */
static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
- struct mdp_rgb_lut_data *cfg)
+ struct fb_cmap *cmap)
{
int rc = 0;
- bool data_validated = false;
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
struct mdp3_dma *dma;
struct mdp3_dma_lut_config lut_config;
- struct fb_cmap *cmap;
dma = mdp3_session->dma;
@@ -1853,6 +2122,37 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
return -EINVAL;
}
+ lut_config.lut_enable = 7;
+ lut_config.lut_sel = mdp3_session->lut_sel;
+ lut_config.lut_position = 1;
+ lut_config.lut_dirty = true;
+
+ if (!mdp3_session->status) {
+ pr_err("display off!\n");
+ return -EPERM;
+ }
+
+ mdp3_clk_enable(1, 0);
+ rc = dma->config_lut(dma, &lut_config, cmap);
+ mdp3_clk_enable(0, 0);
+ if (rc)
+ pr_err("mdp3_ctrl_lut_update failed\n");
+
+ mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2;
+ return rc;
+}
+
+static int mdp3_ctrl_lut_config(struct msm_fb_data_type *mfd,
+ struct mdp_rgb_lut_data *cfg)
+{
+ int rc = 0;
+ bool data_validated = false;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct mdp3_dma *dma;
+ struct fb_cmap *cmap;
+
+ dma = mdp3_session->dma;
+
if (cfg->cmap.start + cfg->cmap.len > MDP_LUT_SIZE) {
pr_err("Invalid arguments\n");
return -EINVAL;
@@ -1864,7 +2164,8 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
return -ENOMEM;
}
- mutex_lock(&mdp3_session->pp_lock);
+ mutex_lock(&mdp3_session->lock);
+ mutex_lock(&dma->pp_lock);
rc = copy_from_user(cmap->red + cfg->cmap.start,
cfg->cmap.red, sizeof(u16) * cfg->cmap.len);
rc |= copy_from_user(cmap->green + cfg->cmap.start,
@@ -1918,8 +2219,11 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
goto exit_err;
}
}
- memcpy(dma->gc_cmap, cmap,
- sizeof(struct fb_cmap));
+ rc = mdp3_copy_lut_buffer(dma->gc_cmap, cmap);
+ if (rc) {
+ pr_err("Could not store GC to cache\n");
+ goto exit_err;
+ }
}
break;
case mdp_rgb_lut_hist:
@@ -1963,8 +2267,11 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
goto exit_err;
}
}
- memcpy(dma->hist_cmap, cmap,
- sizeof(struct fb_cmap));
+ rc = mdp3_copy_lut_buffer(dma->hist_cmap, cmap);
+ if (rc) {
+ pr_err("Could not cache Hist LUT\n");
+ goto exit_err;
+ }
}
break;
default:
@@ -1978,34 +2285,21 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
* of each the individual LUTs need to be applied onto a single LUT
* and applied in HW
*/
- if (dma->lut_sts & MDP3_LUT_HIST_GC_EN)
- mdp3_lut_combine_gain(cmap, dma);
-
- lut_config.lut_enable = 7;
- lut_config.lut_sel = mdp3_session->lut_sel;
- lut_config.lut_position = 0;
- lut_config.lut_dirty = true;
-
- mutex_lock(&mdp3_session->lock);
-
- if (!mdp3_session->status) {
- pr_err("display off!\n");
- mutex_unlock(&mdp3_session->lock);
- rc = -EPERM;
+ if ((dma->lut_sts & MDP3_LUT_HIST_EN) &&
+ (dma->lut_sts & MDP3_LUT_GC_EN)) {
+ rc = mdp3_lut_combine_gain(cmap, dma);
+ if (rc) {
+ pr_err("Combining gains failed rc = %d\n", rc);
goto exit_err;
}
+ }
- mdp3_clk_enable(1, 0);
- rc = dma->config_lut(dma, &lut_config, cmap);
- mdp3_clk_enable(0, 0);
+ rc = mdp3_ctrl_lut_update(mfd, cmap);
if (rc)
- pr_err("mdp3_ctrl_lut_update failed\n");
-
- mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2;
-
- mutex_unlock(&mdp3_session->lock);
+ pr_err("Updating LUT failed! rc = %d\n", rc);
exit_err:
- mutex_unlock(&mdp3_session->pp_lock);
+ mutex_unlock(&dma->pp_lock);
+ mutex_unlock(&mdp3_session->lock);
mdp3_free_lut_buffer(mfd->pdev, (void **) &cmap);
return rc;
}
@@ -2041,17 +2335,77 @@ static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd,
cfg->cmap.start = cmap->start;
cfg->cmap.len = cmap->len;
- mutex_lock(&mdp3_session->pp_lock);
+ mutex_lock(&dma->pp_lock);
rc = copy_to_user(cfg->cmap.red, cmap->red, sizeof(u16) *
MDP_LUT_SIZE);
rc |= copy_to_user(cfg->cmap.green, cmap->green, sizeof(u16) *
MDP_LUT_SIZE);
rc |= copy_to_user(cfg->cmap.blue, cmap->blue, sizeof(u16) *
MDP_LUT_SIZE);
- mutex_unlock(&mdp3_session->pp_lock);
+ mutex_unlock(&dma->pp_lock);
return rc;
}
+/* Invoked from ctrl_on with session lock locked context */
+static void mdp3_ctrl_pp_resume(struct msm_fb_data_type *mfd)
+{
+ struct mdp3_session_data *mdp3_session;
+ struct mdp3_dma *dma;
+ struct fb_cmap *cmap;
+ int rc = 0;
+
+ mdp3_session = mfd->mdp.private1;
+ dma = mdp3_session->dma;
+
+ mutex_lock(&dma->pp_lock);
+ /*
+ * if dma->ccs_config.ccs_enable is set then DMA PP block was enabled
+ * via user space IOCTL.
+ * Then set dma->ccs_config.ccs_dirty flag
+ * Then PP block will be reconfigured when next kickoff comes.
+ */
+ if (dma->ccs_config.ccs_enable)
+ dma->ccs_config.ccs_dirty = true;
+
+ /*
+ * If gamma correction was enabled then we program the LUT registers
+ * with the last configuration data before suspend. If gamma correction
+ * is not enabled then we do not program anything. The LUT from
+ * histogram processing algorithms will program hardware based on new
+ * frame data if they are enabled.
+ */
+ if (dma->lut_sts & MDP3_LUT_GC_EN) {
+
+ rc = mdp3_alloc_lut_buffer(mfd->pdev, (void **)&cmap);
+ if (rc) {
+ pr_err("No memory for GC LUT, rc = %d\n", rc);
+ goto exit_err;
+ }
+
+ if (dma->lut_sts & MDP3_LUT_HIST_EN) {
+ rc = mdp3_lut_combine_gain(cmap, dma);
+ if (rc) {
+ pr_err("Combining the gain failed rc=%d\n", rc);
+ goto exit_err;
+ }
+ } else {
+ rc = mdp3_copy_lut_buffer(cmap, dma->gc_cmap);
+ if (rc) {
+ pr_err("Updating GC failed rc = %d\n", rc);
+ goto exit_err;
+ }
+ }
+
+ rc = mdp3_ctrl_lut_update(mfd, cmap);
+ if (rc)
+ pr_err("GC Lut update failed rc=%d\n", rc);
+exit_err:
+ mdp3_free_lut_buffer(mfd->pdev, (void **)&cmap);
+ }
+
+ mutex_unlock(&dma->pp_lock);
+}
+
static int mdp3_overlay_prepare(struct msm_fb_data_type *mfd,
struct mdp_overlay_list __user *user_ovlist)
{
@@ -2111,7 +2465,7 @@ static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
req = &mdp3_session->req_overlay;
if (!mdp3_session->status && cmd != MSMFB_METADATA_GET &&
- cmd != MSMFB_HISTOGRAM_STOP) {
+ cmd != MSMFB_HISTOGRAM_STOP && cmd != MSMFB_HISTOGRAM) {
pr_err("mdp3_ctrl_ioctl_handler, display off!\n");
return -EPERM;
}
@@ -2139,9 +2493,17 @@ static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
}
break;
case MSMFB_ASYNC_BLIT:
+ if (mdp3_session->in_splash_screen || mdp3_res->idle_pc) {
+ pr_err("%s: reset- in_splash = %d, idle_pc = %d",
+ __func__, mdp3_session->in_splash_screen,
+ mdp3_res->idle_pc);
+ mdp3_ctrl_reset(mfd);
+ }
rc = mdp3_ctrl_async_blit_req(mfd, argp);
break;
case MSMFB_BLIT:
+ if (mdp3_session->in_splash_screen)
+ mdp3_ctrl_reset(mfd);
rc = mdp3_ctrl_blit_req(mfd, argp);
break;
case MSMFB_METADATA_GET:
@@ -2296,7 +2658,6 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
INIT_WORK(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done);
atomic_set(&mdp3_session->vsync_countdown, 0);
mutex_init(&mdp3_session->histo_lock);
- mutex_init(&mdp3_session->pp_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
if (!mdp3_session->dma) {
rc = -ENODEV;
@@ -2346,6 +2707,11 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
pr_err("vsync sysfs group creation failed, ret=%d\n", rc);
goto init_done;
}
+ rc = sysfs_create_group(&dev->kobj, &generic_attr_group);
+ if (rc) {
+ pr_err("generic sysfs group creation failed, ret=%d\n", rc);
+ goto init_done;
+ }
mdp3_session->vsync_event_sd = sysfs_get_dirent(dev->kobj.sd,
"vsync_event");
@@ -2359,6 +2725,10 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
if (rc)
pr_warn("problem creating link to mdp sysfs\n");
+ /* Enable PM runtime */
+ pm_runtime_set_suspended(&mdp3_res->pdev->dev);
+ pm_runtime_enable(&mdp3_res->pdev->dev);
+
kobject_uevent(&dev->kobj, KOBJ_ADD);
pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
@@ -2369,12 +2739,19 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
}
+ /*
+ * Increment the overlay active count.
+ * This is needed to ensure that if idle power collapse kicks in
+ * right away, it would be handled correctly.
+ */
+ atomic_inc(&mdp3_res->active_intf_cnt);
if (splash_mismatch) {
pr_err("splash memory mismatch, stop splash\n");
mdp3_ctrl_off(mfd);
}
mdp3_session->vsync_before_commit = true;
+ mdp3_session->dyn_pu_state = mfd->panel_info->partial_update_enabled;
init_done:
if (IS_ERR_VALUE(rc))
kfree(mdp3_session);
diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.h b/drivers/video/fbdev/msm/mdp3_ctrl.h
index 420907eb1525..0853ed5e7a43 100644
--- a/drivers/video/fbdev/msm/mdp3_ctrl.h
+++ b/drivers/video/fbdev/msm/mdp3_ctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016, 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
@@ -27,7 +27,6 @@
#define MDP3_MAX_BUF_QUEUE 8
#define MDP3_LUT_HIST_EN 0x001
#define MDP3_LUT_GC_EN 0x002
-#define MDP3_LUT_HIST_GC_EN (MDP3_LUT_HIST_EN | MDP3_LUT_GC_EN)
struct mdp3_buffer_queue {
struct mdp3_img_data img_data[MDP3_MAX_BUF_QUEUE];
@@ -56,9 +55,7 @@ struct mdp3_session_data {
atomic_t dma_done_cnt;
int histo_status;
struct mutex histo_lock;
- struct mutex pp_lock;
int lut_sel;
- int cc_vect_sel;
bool vsync_before_commit;
bool first_commit;
int clk_on;
@@ -67,6 +64,8 @@ struct mdp3_session_data {
int vsync_enabled;
atomic_t vsync_countdown; /* Used to count down */
bool in_splash_screen;
+ bool esd_recovery;
+ int dyn_pu_state; /* dynamic partial update status */
bool dma_active;
struct completion dma_completion;
diff --git a/drivers/video/fbdev/msm/mdp3_dma.c b/drivers/video/fbdev/msm/mdp3_dma.c
index 8bac9d84edde..d4c83d6e33f0 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.c
+++ b/drivers/video/fbdev/msm/mdp3_dma.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016, 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,6 +16,7 @@
#include "mdp3.h"
#include "mdp3_dma.h"
#include "mdp3_hwio.h"
+#include "mdss_debug.h"
#define DMA_STOP_POLL_SLEEP_US 1000
#define DMA_STOP_POLL_TIMEOUT_US 200000
@@ -252,26 +253,6 @@ static void mdp3_dma_done_notifier(struct mdp3_dma *dma,
spin_unlock_irqrestore(&dma->dma_lock, flag);
}
-static void mdp3_dma_clk_auto_gating(struct mdp3_dma *dma, int enable)
-{
- u32 cgc;
- int clock_bit = 10;
-
- clock_bit += dma->dma_sel;
-
- if (enable) {
- cgc = MDP3_REG_READ(MDP3_REG_CGC_EN);
- cgc |= BIT(clock_bit);
- MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
-
- } else {
- cgc = MDP3_REG_READ(MDP3_REG_CGC_EN);
- cgc &= ~BIT(clock_bit);
- MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
- }
-}
-
-
int mdp3_dma_sync_config(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config, struct mdp3_tear_check *te)
{
@@ -318,7 +299,8 @@ int mdp3_dma_sync_config(struct mdp3_dma *dma,
static int mdp3_dmap_config(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
- struct mdp3_dma_output_config *output_config)
+ struct mdp3_dma_output_config *output_config,
+ bool splash_screen_active)
{
u32 dma_p_cfg_reg, dma_p_size, dma_p_out_xy;
@@ -334,22 +316,23 @@ static int mdp3_dmap_config(struct mdp3_dma *dma,
dma_p_size = source_config->width | (source_config->height << 16);
dma_p_out_xy = source_config->x | (source_config->y << 16);
-
- MDP3_REG_WRITE(MDP3_REG_DMA_P_CONFIG, dma_p_cfg_reg);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_SIZE, dma_p_size);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)source_config->buf);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_Y_STRIDE, source_config->stride);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_OUT_XY, dma_p_out_xy);
-
- MDP3_REG_WRITE(MDP3_REG_DMA_P_FETCH_CFG, 0x40);
+ if (!splash_screen_active) {
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CONFIG, dma_p_cfg_reg);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_SIZE, dma_p_size);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR,
+ (u32)source_config->buf);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_Y_STRIDE,
+ source_config->stride);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_OUT_XY, dma_p_out_xy);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_FETCH_CFG, 0x40);
+ }
dma->source_config = *source_config;
dma->output_config = *output_config;
- dma->roi.w = dma->source_config.width;
- dma->roi.h = dma->source_config.height;
- dma->roi.x = dma->source_config.x;
- dma->roi.y = dma->source_config.y;
- mdp3_irq_enable(MDP3_INTR_LCDC_UNDERFLOW);
+
+ if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD)
+ mdp3_irq_enable(MDP3_INTR_LCDC_UNDERFLOW);
+
mdp3_dma_callback_setup(dma);
return 0;
}
@@ -374,7 +357,8 @@ static void mdp3_dmap_config_source(struct mdp3_dma *dma)
static int mdp3_dmas_config(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
- struct mdp3_dma_output_config *output_config)
+ struct mdp3_dma_output_config *output_config,
+ bool splash_screen_active)
{
u32 dma_s_cfg_reg, dma_s_size, dma_s_out_xy;
@@ -391,14 +375,16 @@ static int mdp3_dmas_config(struct mdp3_dma *dma,
dma_s_size = source_config->width | (source_config->height << 16);
dma_s_out_xy = source_config->x | (source_config->y << 16);
- MDP3_REG_WRITE(MDP3_REG_DMA_S_CONFIG, dma_s_cfg_reg);
- MDP3_REG_WRITE(MDP3_REG_DMA_S_SIZE, dma_s_size);
- MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_ADDR, (u32)source_config->buf);
- MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_Y_STRIDE, source_config->stride);
- MDP3_REG_WRITE(MDP3_REG_DMA_S_OUT_XY, dma_s_out_xy);
-
- MDP3_REG_WRITE(MDP3_REG_SECONDARY_RD_PTR_IRQ, 0x10);
-
+ if (!splash_screen_active) {
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_CONFIG, dma_s_cfg_reg);
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_SIZE, dma_s_size);
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_ADDR,
+ (u32)source_config->buf);
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_Y_STRIDE,
+ source_config->stride);
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_OUT_XY, dma_s_out_xy);
+ MDP3_REG_WRITE(MDP3_REG_SECONDARY_RD_PTR_IRQ, 0x10);
+ }
dma->source_config = *source_config;
dma->output_config = *output_config;
@@ -452,46 +438,7 @@ static int mdp3_dmap_cursor_config(struct mdp3_dma *dma,
return 0;
}
-static void mdp3_ccs_update(struct mdp3_dma *dma)
-{
- u32 cc_config;
- int updated = 0;
-
- cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
-
- if (dma->ccs_config.ccs_dirty) {
- cc_config &= DMA_CCS_CONFIG_MASK;
- if (dma->ccs_config.ccs_enable)
- cc_config |= BIT(3);
- else
- cc_config &= ~BIT(3);
- cc_config |= dma->ccs_config.ccs_sel << 5;
- cc_config |= dma->ccs_config.pre_bias_sel << 6;
- cc_config |= dma->ccs_config.post_bias_sel << 7;
- cc_config |= dma->ccs_config.pre_limit_sel << 8;
- cc_config |= dma->ccs_config.post_limit_sel << 9;
- dma->ccs_config.ccs_dirty = false;
- updated = 1;
- }
-
- if (dma->lut_config.lut_dirty) {
- cc_config &= DMA_LUT_CONFIG_MASK;
- cc_config |= dma->lut_config.lut_enable;
- cc_config |= dma->lut_config.lut_position << 4;
- cc_config |= dma->lut_config.lut_sel << 10;
- dma->lut_config.lut_dirty = false;
- updated = 1;
- }
- if (updated) {
- MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
-
- /* Make sure ccs configuration update is done before continuing
- with the DMA transfer */
- wmb();
- }
-}
-
-static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
+static int mdp3_dmap_ccs_config_internal(struct mdp3_dma *dma,
struct mdp3_dma_color_correct_config *config,
struct mdp3_dma_ccs *ccs)
{
@@ -542,10 +489,77 @@ static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
addr += 4;
}
}
+ return 0;
+}
+
+static void mdp3_ccs_update(struct mdp3_dma *dma, bool from_kickoff)
+{
+ u32 cc_config;
+ bool ccs_updated = false, lut_updated = false;
+ struct mdp3_dma_ccs ccs;
+
+ cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
+
+ if (dma->ccs_config.ccs_dirty) {
+ cc_config &= DMA_CCS_CONFIG_MASK;
+ if (dma->ccs_config.ccs_enable)
+ cc_config |= BIT(3);
+ else
+ cc_config &= ~BIT(3);
+ cc_config |= dma->ccs_config.ccs_sel << 5;
+ cc_config |= dma->ccs_config.pre_bias_sel << 6;
+ cc_config |= dma->ccs_config.post_bias_sel << 7;
+ cc_config |= dma->ccs_config.pre_limit_sel << 8;
+ cc_config |= dma->ccs_config.post_limit_sel << 9;
+ /*
+ * CCS dirty flag should be reset when call is made from frame
+ * kickoff, or else upon resume the flag would be dirty and LUT
+ * config could call this function thereby causing no register
+ * programming for CCS, which will cause screen to go dark
+ */
+ if (from_kickoff)
+ dma->ccs_config.ccs_dirty = false;
+ ccs_updated = true;
+ }
+
+ if (dma->lut_config.lut_dirty) {
+ cc_config &= DMA_LUT_CONFIG_MASK;
+ cc_config |= dma->lut_config.lut_enable;
+ cc_config |= dma->lut_config.lut_position << 4;
+ cc_config |= dma->lut_config.lut_sel << 10;
+ dma->lut_config.lut_dirty = false;
+ lut_updated = true;
+ }
+
+ if (ccs_updated && from_kickoff) {
+ ccs.mv = dma->ccs_cache.csc_data.csc_mv;
+ ccs.pre_bv = dma->ccs_cache.csc_data.csc_pre_bv;
+ ccs.post_bv = dma->ccs_cache.csc_data.csc_post_bv;
+ ccs.pre_lv = dma->ccs_cache.csc_data.csc_pre_lv;
+ ccs.post_lv = dma->ccs_cache.csc_data.csc_post_lv;
+ mdp3_dmap_ccs_config_internal(dma, &dma->ccs_config, &ccs);
+ }
+
+ if (lut_updated || ccs_updated) {
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
+ /*
+ * Make sure ccs configuration update is done before continuing
+ * with the DMA transfer
+ */
+ wmb();
+ }
+}
+
+static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
+ struct mdp3_dma_color_correct_config *config,
+ struct mdp3_dma_ccs *ccs)
+{
+ mdp3_dmap_ccs_config_internal(dma, config, ccs);
+
dma->ccs_config = *config;
if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD)
- mdp3_ccs_update(dma);
+ mdp3_ccs_update(dma, false);
return 0;
}
@@ -574,7 +588,7 @@ static int mdp3_dmap_lut_config(struct mdp3_dma *dma,
dma->lut_config = *config;
if (dma->output_config.out_sel != MDP3_DMA_OUTPUT_SEL_DSI_CMD)
- mdp3_ccs_update(dma);
+ mdp3_ccs_update(dma, false);
return 0;
}
@@ -633,45 +647,64 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf,
int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
struct mdss_panel_data *panel;
int rc = 0;
+ int retry_count = 2;
+ ATRACE_BEGIN(__func__);
pr_debug("mdp3_dmap_update\n");
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
if (intf->active) {
+ ATRACE_BEGIN("mdp3_wait_for_dma_comp");
+retry_dma_done:
rc = wait_for_completion_timeout(&dma->dma_comp,
KOFF_TIMEOUT);
- if (rc <= 0) {
- WARN(1, "cmd kickoff timed out (%d)\n", rc);
+ if (rc <= 0 && --retry_count) {
+ int vsync_status;
+
+ vsync_status = (1 << MDP3_INTR_DMA_P_DONE) &
+ MDP3_REG_READ(MDP3_REG_INTR_STATUS);
+ if (!vsync_status) {
+ pr_err("%s: cmd timeout retry cnt %d\n",
+ __func__, retry_count);
+ goto retry_dma_done;
+ }
rc = -1;
}
+ 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");
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO && intf->active)
+ pr_err("configuring dma source while it is active\n");
dma->dma_config_source(dma);
if (data) {
panel = (struct mdss_panel_data *)data;
- if (panel->event_handler)
+ if (panel->event_handler) {
panel->event_handler(panel,
MDSS_EVENT_ENABLE_PARTIAL_ROI, NULL);
+ panel->event_handler(panel,
+ MDSS_EVENT_DSI_STREAM_SIZE, NULL);
+ }
}
dma->update_src_cfg = false;
}
+ mutex_lock(&dma->pp_lock);
+ if (dma->ccs_config.ccs_dirty)
+ mdp3_ccs_update(dma, true);
+ mutex_unlock(&dma->pp_lock);
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)(buf +
dma->roi.y * dma->source_config.stride +
dma->roi.x * dma_bpp(dma->source_config.format)));
dma->source_config.buf = (int)buf;
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
- mdp3_ccs_update(dma);
MDP3_REG_WRITE(MDP3_REG_DMA_P_START, 1);
}
if (!intf->active) {
- pr_debug("mdp3_dmap_update start interface\n");
+ pr_debug("%s start interface\n", __func__);
intf->start(intf);
}
@@ -682,14 +715,28 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf,
spin_unlock_irqrestore(&dma->dma_lock, flag);
mdp3_dma_callback_enable(dma, cb_type);
- pr_debug("mdp3_dmap_update wait for vsync_comp in\n");
+ pr_debug("%s wait for vsync_comp\n", __func__);
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ ATRACE_BEGIN("mdp3_wait_for_vsync_comp");
+retry_vsync:
rc = wait_for_completion_timeout(&dma->vsync_comp,
KOFF_TIMEOUT);
- if (rc <= 0)
+ pr_err("%s VID DMA Buff Addr %p\n", __func__, buf);
+ if (rc <= 0 && --retry_count) {
+ int vsync = MDP3_REG_READ(MDP3_REG_INTR_STATUS) &
+ (1 << MDP3_INTR_LCDC_START_OF_FRAME);
+
+ if (!vsync) {
+ pr_err("%s trying again count = %d\n",
+ __func__, retry_count);
+ goto retry_vsync;
+ }
rc = -1;
+ }
+ ATRACE_END("mdp3_wait_for_vsync_comp");
}
- pr_debug("mdp3_dmap_update wait for vsync_comp out\n");
+ pr_debug("$%s wait for vsync_comp out\n", __func__);
+ ATRACE_END(__func__);
return rc;
}
@@ -834,7 +881,6 @@ static int mdp3_dmap_histo_reset(struct mdp3_dma *dma)
init_completion(&dma->histo_comp);
- mdp3_dma_clk_auto_gating(dma, 0);
MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, BIT(0)|BIT(1));
MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
@@ -856,7 +902,6 @@ static int mdp3_dmap_histo_reset(struct mdp3_dma *dma)
ret = 0;
}
mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
- mdp3_dma_clk_auto_gating(dma, 1);
return ret;
}
@@ -902,6 +947,38 @@ static int mdp3_dmap_histo_op(struct mdp3_dma *dma, u32 op)
return ret;
}
+bool mdp3_dmap_busy(void)
+{
+ u32 val;
+
+ val = MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS);
+ pr_err("%s DMAP Status %s\n", __func__,
+ (val & MDP3_DMA_P_BUSY_BIT) ? "BUSY":"IDLE");
+ return val & MDP3_DMA_P_BUSY_BIT;
+}
+
+/*
+ * During underrun DMA_P registers are reset. Reprogramming CSC to prevent
+ * black screen
+ */
+static void mdp3_dmap_underrun_worker(struct work_struct *work)
+{
+ struct mdp3_dma *dma;
+
+ dma = container_of(work, struct mdp3_dma, underrun_work);
+ mutex_lock(&dma->pp_lock);
+ if (dma->ccs_config.ccs_enable && dma->ccs_config.ccs_dirty) {
+ dma->cc_vect_sel = (dma->cc_vect_sel + 1) % 2;
+ dma->ccs_config.ccs_sel = dma->cc_vect_sel;
+ dma->ccs_config.pre_limit_sel = dma->cc_vect_sel;
+ dma->ccs_config.post_limit_sel = dma->cc_vect_sel;
+ dma->ccs_config.pre_bias_sel = dma->cc_vect_sel;
+ dma->ccs_config.post_bias_sel = dma->cc_vect_sel;
+ mdp3_ccs_update(dma, true);
+ }
+ mutex_unlock(&dma->pp_lock);
+}
+
static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf)
{
unsigned long flag;
@@ -995,6 +1072,8 @@ int mdp3_dma_init(struct mdp3_dma *dma)
dma->dma_done_notifier = mdp3_dma_done_notifier;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
+ dma->busy = mdp3_dmap_busy;
+ INIT_WORK(&dma->underrun_work, mdp3_dmap_underrun_worker);
break;
case MDP3_DMA_S:
dma->dma_config = mdp3_dmas_config;
diff --git a/drivers/video/fbdev/msm/mdp3_dma.h b/drivers/video/fbdev/msm/mdp3_dma.h
index 60c9e1dfb67f..fb719f6ba62e 100644
--- a/drivers/video/fbdev/msm/mdp3_dma.h
+++ b/drivers/video/fbdev/msm/mdp3_dma.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016, 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,6 +16,7 @@
#include <linux/notifier.h>
#include <linux/sched.h>
+#include <linux/msm_mdp.h>
#define MDP_HISTOGRAM_BL_SCALE_MAX 1024
#define MDP_HISTOGRAM_BL_LEVEL_MAX 255
@@ -268,6 +269,12 @@ struct mdp3_dma {
struct mdp3_dma_cursor cursor;
struct mdp3_dma_color_correct_config ccs_config;
+ struct mdp_csc_cfg_data ccs_cache;
+ int cc_vect_sel;
+
+ struct work_struct underrun_work;
+ struct mutex pp_lock;
+
struct mdp3_dma_lut_config lut_config;
struct mdp3_dma_histogram_config histogram_config;
int histo_state;
@@ -281,9 +288,12 @@ struct mdp3_dma {
struct fb_cmap *gc_cmap;
struct fb_cmap *hist_cmap;
+ bool (*busy)(void);
+
int (*dma_config)(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
- struct mdp3_dma_output_config *output_config);
+ struct mdp3_dma_output_config *output_config,
+ bool splash_screen_active);
int (*dma_sync_config)(struct mdp3_dma *dma, struct mdp3_dma_source
*source_config, struct mdp3_tear_check *te);
diff --git a/drivers/video/fbdev/msm/mdp3_hwio.h b/drivers/video/fbdev/msm/mdp3_hwio.h
index 83615a786e81..056355c17823 100644
--- a/drivers/video/fbdev/msm/mdp3_hwio.h
+++ b/drivers/video/fbdev/msm/mdp3_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2016, 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,6 +30,7 @@
#define MDP3_REG_PRIMARY_VSYNC_INIT_VAL 0x0328
#define MDP3_REG_SECONDARY_VSYNC_INIT_VAL 0x032c
#define MDP3_REG_EXTERNAL_VSYNC_INIT_VAL 0x0330
+#define MDP3_REG_AUTOREFRESH_CONFIG_P 0x034C
#define MDP3_REG_SYNC_THRESH_0 0x0200
#define MDP3_REG_SYNC_THRESH_1 0x0204
#define MDP3_REG_SYNC_THRESH_2 0x0208
@@ -63,6 +64,17 @@
/*clock control*/
#define MDP3_REG_CGC_EN 0x0100
+#define MDP3_VBIF_REG_FORCE_EN 0x0004
+
+/* QOS Remapper */
+#define MDP3_DMA_P_QOS_REMAPPER 0x90090
+#define MDP3_DMA_P_WATERMARK_0 0x90094
+#define MDP3_DMA_P_WATERMARK_1 0x90098
+#define MDP3_DMA_P_WATERMARK_2 0x9009C
+#define MDP3_PANIC_ROBUST_CTRL 0x900A0
+#define MDP3_PANIC_LUT0 0x900A4
+#define MDP3_PANIC_LUT1 0x900A8
+#define MDP3_ROBUST_LUT 0x900AC
/*danger safe*/
#define MDP3_PANIC_ROBUST_CTRL 0x900A0
@@ -344,4 +356,6 @@ enum {
#define MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT BIT(1)
#define MDP3_PPP_DONE MDP3_INTR_DP0_ROI_DONE
+#define MDP3_DMA_P_BUSY_BIT BIT(6)
+
#endif /* MDP3_HWIO_H */
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.c b/drivers/video/fbdev/msm/mdp3_ppp.c
index 4bc71cccd289..eaacdd875747 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2013-2014 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2007, 2013-2014, 2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2007 Google Incorporated
*
* This software is licensed under the terms of the GNU General Public
@@ -31,6 +31,7 @@
#include "mdp3_ppp.h"
#include "mdp3_hwio.h"
#include "mdp3.h"
+#include "mdss_debug.h"
#define MDP_IS_IMGTYPE_BAD(x) ((x) >= MDP_IMGTYPE_LIMIT)
#define MDP_RELEASE_BW_TIMEOUT 50
@@ -40,6 +41,14 @@
#define MDP_PPP_MAX_READ_WRITE 3
#define ENABLE_SOLID_FILL 0x2
#define DISABLE_SOLID_FILL 0x0
+#define BLEND_LATENCY 3
+#define CSC_LATENCY 1
+
+#define CLK_FUDGE_NUM 12
+#define CLK_FUDGE_DEN 10
+
+#define YUV_BW_FUDGE_NUM 10
+#define YUV_BW_FUDGE_DEN 10
struct ppp_resource ppp_res;
@@ -108,11 +117,20 @@ struct ppp_status {
};
static struct ppp_status *ppp_stat;
+static bool is_blit_optimization_possible(struct blit_req_list *req, int indx);
+
+static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
+{
+ u64 result = (val * (u64)numer);
+ do_div(result, denom);
+ return result;
+}
int ppp_get_bpp(uint32_t format, uint32_t fb_format)
{
int bpp = -EINVAL;
+
if (format == MDP_FB_FORMAT)
format = fb_format;
@@ -126,12 +144,22 @@ int mdp3_ppp_get_img(struct mdp_img *img, struct mdp_blit_req *req,
struct mdp3_img_data *data)
{
struct msmfb_data fb_data;
+ uint32_t stride;
+ int bpp = ppp_bpp(img->format);
+
+ if (bpp <= 0) {
+ pr_err("%s incorrect format %d\n", __func__, img->format);
+ return -EINVAL;
+ }
fb_data.flags = img->priv;
fb_data.memory_id = img->memory_id;
fb_data.offset = 0;
- return mdp3_get_img(&fb_data, data);
+ stride = img->width * bpp;
+ data->padding = 16 * stride;
+
+ return mdp3_get_img(&fb_data, data, MDP3_CLIENT_PPP);
}
/* Check format */
@@ -233,6 +261,12 @@ int mdp3_ppp_verify_scale(struct mdp_blit_req *req)
/* operation check */
int mdp3_ppp_verify_op(struct mdp_blit_req *req)
{
+ /*
+ * MDP_DEINTERLACE & MDP_SHARPENING Flags are not valid for MDP3
+ * so using them together for MDP_SMART_BLIT.
+ */
+ if ((req->flags & MDP_SMART_BLIT) == MDP_SMART_BLIT)
+ return 0;
if (req->flags & MDP_DEINTERLACE) {
pr_err("\n%s(): deinterlace not supported", __func__);
return -EINVAL;
@@ -327,34 +361,12 @@ void mdp3_ppp_kickoff(void)
init_completion(&ppp_stat->ppp_comp);
mdp3_irq_enable(MDP3_PPP_DONE);
ppp_enable();
+ ATRACE_BEGIN("mdp3_wait_for_ppp_comp");
mdp3_ppp_pipe_wait();
+ ATRACE_END("mdp3_wait_for_ppp_comp");
mdp3_irq_disable(MDP3_PPP_DONE);
}
-u32 mdp3_clk_calc(struct msm_fb_data_type *mfd, struct blit_req_list *lreq)
-{
- struct mdss_panel_info *panel_info = mfd->panel_info;
- int i, lcount = 0;
- struct mdp_blit_req *req;
- u32 total_pixel;
- u32 mdp_clk_rate = MDP_CORE_CLK_RATE_SVS;
-
- total_pixel = panel_info->xres * panel_info->yres;
- if (total_pixel > SVS_MAX_PIXEL)
- return MDP_CORE_CLK_RATE_MAX;
-
- for (i = 0; i < lcount; i++) {
- req = &(lreq->req_list[i]);
-
- if (req->src_rect.h != req->dst_rect.h ||
- req->src_rect.w != req->dst_rect.w) {
- mdp_clk_rate = MDP_CORE_CLK_RATE_MAX;
- break;
- }
- }
- return mdp_clk_rate;
-}
-
struct bpp_info {
int bpp_num;
int bpp_den;
@@ -413,6 +425,84 @@ int mdp3_get_bpp_info(int format, struct bpp_info *bpp)
return rc;
}
+bool mdp3_is_blend(struct mdp_blit_req *req)
+{
+ if ((req->transp_mask != MDP_TRANSP_NOP) ||
+ (req->alpha < MDP_ALPHA_NOP) ||
+ (req->src.format == MDP_ARGB_8888) ||
+ (req->src.format == MDP_BGRA_8888) ||
+ (req->src.format == MDP_RGBA_8888))
+ return true;
+ return false;
+}
+
+bool mdp3_is_scale(struct mdp_blit_req *req)
+{
+ if (req->flags & MDP_ROT_90) {
+ if (req->src_rect.w != req->dst_rect.h ||
+ req->src_rect.h != req->dst_rect.w)
+ return true;
+ } else {
+ if (req->src_rect.h != req->dst_rect.h ||
+ req->src_rect.w != req->dst_rect.w)
+ return true;
+ }
+ return false;
+}
+
+u32 mdp3_clk_calc(struct msm_fb_data_type *mfd,
+ struct blit_req_list *lreq, u32 fps)
+{
+ int i, lcount = 0;
+ struct mdp_blit_req *req;
+ u64 mdp_clk_rate = 0;
+ u32 scale_x = 0, scale_y = 0, scale = 0;
+ u32 blend_l, csc_l;
+
+ lcount = lreq->count;
+
+ blend_l = 100 * BLEND_LATENCY;
+ csc_l = 100 * CSC_LATENCY;
+
+ for (i = 0; i < lcount; i++) {
+ req = &(lreq->req_list[i]);
+
+ if (req->flags & MDP_SMART_BLIT)
+ continue;
+
+ if (mdp3_is_scale(req)) {
+ if (req->flags & MDP_ROT_90) {
+ scale_x = 100 * req->src_rect.h /
+ req->dst_rect.w;
+ scale_y = 100 * req->src_rect.w /
+ req->dst_rect.h;
+ } else {
+ scale_x = 100 * req->src_rect.w /
+ req->dst_rect.w;
+ scale_y = 100 * req->src_rect.h /
+ req->dst_rect.h;
+ }
+ scale = max(scale_x, scale_y);
+ }
+ scale = scale >= 100 ? scale : 100;
+ if (mdp3_is_blend(req))
+ scale = max(scale, blend_l);
+
+ if (!check_if_rgb(req->src.format))
+ scale = max(scale, csc_l);
+
+ mdp_clk_rate += (req->src_rect.w * req->src_rect.h *
+ scale / 100) * fps;
+ }
+ mdp_clk_rate += (ppp_res.solid_fill_pixel * fps);
+ mdp_clk_rate = fudge_factor(mdp_clk_rate,
+ CLK_FUDGE_NUM, CLK_FUDGE_DEN);
+ pr_debug("mdp_clk_rate for ppp = %llu\n", mdp_clk_rate);
+ mdp_clk_rate = mdp3_clk_round_off(mdp_clk_rate);
+
+ return mdp_clk_rate;
+}
+
u64 mdp3_adjust_scale_factor(struct mdp_blit_req *req, u32 bw_req, int bpp)
{
int src_h, src_w;
@@ -424,67 +514,145 @@ u64 mdp3_adjust_scale_factor(struct mdp_blit_req *req, u32 bw_req, int bpp)
dst_h = req->dst_rect.h;
dst_w = req->dst_rect.w;
- if ((!(req->flags & MDP_ROT_90) && src_h == dst_h && src_w == dst_w) ||
- ((req->flags & MDP_ROT_90) && src_h == dst_w && src_w == dst_h))
+ if ((!(req->flags & MDP_ROT_90) && src_h == dst_h &&
+ src_w == dst_w) || ((req->flags & MDP_ROT_90) &&
+ src_h == dst_w && src_w == dst_h))
return bw_req;
bw_req = (bw_req + (bw_req * dst_h) / (4 * src_h));
bw_req = (bw_req + (bw_req * dst_w) / (4 * src_w) +
(bw_req * dst_w) / (bpp * src_w));
-
return bw_req;
}
-int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd, struct blit_req_list *lreq)
+int mdp3_calc_ppp_res(struct msm_fb_data_type *mfd,
+ struct blit_req_list *lreq)
{
struct mdss_panel_info *panel_info = mfd->panel_info;
int i, lcount = 0;
struct mdp_blit_req *req;
struct bpp_info bpp;
- u32 src_read_bw = 0;
- u32 dst_read_bw = 0;
+ u64 src_read_bw = 0;
+ u32 bg_read_bw = 0;
u32 dst_write_bw = 0;
u64 honest_ppp_ab = 0;
- u32 fps;
+ u32 fps = 0;
+ int smart_blit_fg_indx = -1;
+ u32 smart_blit_bg_read_bw = 0;
+ ATRACE_BEGIN(__func__);
lcount = lreq->count;
if (lcount == 0) {
pr_err("Blit with request count 0, continue to recover!!!\n");
+ ATRACE_END(__func__);
return 0;
}
-
- /* Set FPS to mipi rate as currently there is no way to get this */
- fps = panel_info->mipi.frame_rate;
+ if (lreq->req_list[0].flags & MDP_SOLID_FILL) {
+ req = &(lreq->req_list[0]);
+ mdp3_get_bpp_info(req->dst.format, &bpp);
+ ppp_res.solid_fill_pixel += req->dst_rect.w * req->dst_rect.h;
+ ppp_res.solid_fill_byte += req->dst_rect.w * req->dst_rect.h *
+ bpp.bpp_num / bpp.bpp_den;
+ if ((panel_info->yres/2 > req->dst_rect.h) ||
+ (mdp3_res->solid_fill_vote_en)) {
+ pr_debug("Solid fill less than H/2 or fill vote %d\n",
+ mdp3_res->solid_fill_vote_en);
+ ATRACE_END(__func__);
+ return 0;
+ }
+ }
for (i = 0; i < lcount; i++) {
+ /* Set Smart blit flag before BW calculation */
+ is_blit_optimization_possible(lreq, i);
req = &(lreq->req_list[i]);
+ if (req->fps > 0 && req->fps <= panel_info->mipi.frame_rate) {
+ if (fps == 0)
+ fps = req->fps;
+ else
+ fps = panel_info->mipi.frame_rate;
+ }
+
mdp3_get_bpp_info(req->src.format, &bpp);
- src_read_bw = req->src_rect.w * req->src_rect.h *
+ if (lreq->req_list[i].flags & MDP_SMART_BLIT) {
+ /*
+ * Flag for smart blit FG layer index
+ * If blit request at index "n" has
+ * MDP_SMART_BLIT flag set then it will be used as BG
+ * layer in smart blit and request at index "n+1"
+ * will be used as FG layer
+ */
+ smart_blit_fg_indx = i + 1;
+ bg_read_bw = req->src_rect.w * req->src_rect.h *
bpp.bpp_num / bpp.bpp_den;
- src_read_bw = mdp3_adjust_scale_factor(req,
- src_read_bw, bpp.bpp_pln);
-
- mdp3_get_bpp_info(req->dst.format, &bpp);
- dst_read_bw = req->dst_rect.w * req->dst_rect.h *
+ bg_read_bw = mdp3_adjust_scale_factor(req,
+ bg_read_bw, bpp.bpp_pln);
+ /* Cache read BW of smart blit BG layer */
+ smart_blit_bg_read_bw = bg_read_bw;
+ } else {
+ src_read_bw = req->src_rect.w * req->src_rect.h *
+ bpp.bpp_num / bpp.bpp_den;
+ src_read_bw = mdp3_adjust_scale_factor(req,
+ src_read_bw, bpp.bpp_pln);
+ if (!(check_if_rgb(req->src.format))) {
+ src_read_bw = fudge_factor(src_read_bw,
+ YUV_BW_FUDGE_NUM,
+ YUV_BW_FUDGE_DEN);
+ }
+ mdp3_get_bpp_info(req->dst.format, &bpp);
+
+ if (smart_blit_fg_indx == i) {
+ bg_read_bw = smart_blit_bg_read_bw;
+ smart_blit_fg_indx = -1;
+ } else {
+ if ((req->transp_mask != MDP_TRANSP_NOP) ||
+ (req->alpha < MDP_ALPHA_NOP) ||
+ (req->src.format == MDP_ARGB_8888) ||
+ (req->src.format == MDP_BGRA_8888) ||
+ (req->src.format == MDP_RGBA_8888)) {
+ bg_read_bw = req->dst_rect.w *
+ req->dst_rect.h *
bpp.bpp_num / bpp.bpp_den;
- dst_read_bw = mdp3_adjust_scale_factor(req,
- dst_read_bw, bpp.bpp_pln);
-
- dst_write_bw = req->dst_rect.w * req->dst_rect.h *
+ bg_read_bw = mdp3_adjust_scale_factor(
+ req, bg_read_bw,
+ bpp.bpp_pln);
+ } else {
+ bg_read_bw = 0;
+ }
+ }
+ dst_write_bw = req->dst_rect.w * req->dst_rect.h *
bpp.bpp_num / bpp.bpp_den;
- honest_ppp_ab += (src_read_bw + dst_read_bw + dst_write_bw);
+ honest_ppp_ab += (src_read_bw + bg_read_bw +
+ dst_write_bw);
+ }
}
- honest_ppp_ab = honest_ppp_ab * fps;
+ if (fps == 0)
+ fps = panel_info->mipi.frame_rate;
+
+ if (lreq->req_list[0].flags & MDP_SOLID_FILL) {
+ honest_ppp_ab = ppp_res.solid_fill_byte * 4;
+ pr_debug("solid fill honest_ppp_ab %llu\n", honest_ppp_ab);
+ } else {
+ honest_ppp_ab += ppp_res.solid_fill_byte;
+ mdp3_res->solid_fill_vote_en = true;
+ }
+
+ honest_ppp_ab = honest_ppp_ab * fps;
if (honest_ppp_ab != ppp_res.next_ab) {
- pr_debug("bandwidth vote update for ppp: ab = %llx\n",
- honest_ppp_ab);
ppp_res.next_ab = honest_ppp_ab;
ppp_res.next_ib = honest_ppp_ab;
ppp_stat->bw_update = true;
+ pr_debug("solid fill ab = %llx, total ab = %llx ",
+ (ppp_res.solid_fill_byte * fps), honest_ppp_ab);
+ pr_debug("(%d fps) Solid_fill_vote %d\n",
+ fps, mdp3_res->solid_fill_vote_en);
+ ATRACE_INT("mdp3_ppp_bus_quota", honest_ppp_ab);
}
- ppp_res.clk_rate = mdp3_clk_calc(mfd, lreq);
+ ppp_res.clk_rate = mdp3_clk_calc(mfd, lreq, fps);
+ ATRACE_INT("mdp3_ppp_clk_rate", ppp_res.clk_rate);
+ ATRACE_END(__func__);
return 0;
}
@@ -543,7 +711,16 @@ void mdp3_start_ppp(struct ppp_blit_op *blit_op)
MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL,
DISABLE_SOLID_FILL);
}
+ /* Skip PPP kickoff for SMART_BLIT BG layer */
+ if (blit_op->mdp_op & MDPOP_SMART_BLIT)
+ pr_debug("Skip mdp3_ppp_kickoff\n");
+ else
mdp3_ppp_kickoff();
+
+ if (!(blit_op->solid_fill)) {
+ ppp_res.solid_fill_pixel = 0;
+ ppp_res.solid_fill_byte = 0;
+ }
}
static int solid_fill_workaround(struct mdp_blit_req *req,
@@ -572,9 +749,10 @@ static int solid_fill_workaround(struct mdp_blit_req *req,
blit_op->dst.roi.width = (blit_op->dst.roi.width / 2) * 2;
blit_op->src.roi.width = (blit_op->src.roi.width / 2) * 2;
+ /* Set src format to RGBX, to avoid ppp hang issues */
+ blit_op->src.color_fmt = MDP_RGBX_8888;
+
/* Avoid RGBA format, as it could hang ppp during solid fill */
- if (blit_op->src.color_fmt == MDP_RGBA_8888)
- blit_op->src.color_fmt = MDP_RGBX_8888;
if (blit_op->dst.color_fmt == MDP_RGBA_8888)
blit_op->dst.color_fmt = MDP_RGBX_8888;
return 0;
@@ -611,6 +789,7 @@ static int mdp3_ppp_process_req(struct ppp_blit_op *blit_op,
blit_op->src.roi.height = req->src_rect.h;
blit_op->src.prop.width = req->src.width;
+ blit_op->src.prop.height = req->src.height;
blit_op->src.color_fmt = req->src.format;
@@ -691,6 +870,10 @@ static int mdp3_ppp_process_req(struct ppp_blit_op *blit_op,
} else {
blit_op->solid_fill = false;
}
+
+ if (req->flags & MDP_SMART_BLIT)
+ blit_op->mdp_op |= MDPOP_SMART_BLIT;
+
return ret;
}
@@ -714,14 +897,16 @@ static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op,
/* if it's out of scale range... */
if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR)
+ blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR)
blit_op->src.roi.width =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ (MDP_SCALE_Q_FACTOR *
+ blit_op->dst.roi.height) /
MDP_MAX_X_SCALE_FACTOR;
else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR)
+ blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR)
blit_op->src.roi.width =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
+ (MDP_SCALE_Q_FACTOR *
+ blit_op->dst.roi.height) /
MDP_MIN_X_SCALE_FACTOR;
mdp3_start_ppp(blit_op);
@@ -741,9 +926,8 @@ static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op,
}
if ((dst_h < 0) || (src_w < 0))
- pr_err
- ("msm_fb: mdp_blt_ex() unexpected result! line:%d\n",
- __LINE__);
+ pr_err("msm_fb: mdp_blt_ex() unexpected result! line:%d\n",
+ __LINE__);
/* remainder update */
if ((dst_h > 0) && (src_w > 0)) {
@@ -753,26 +937,25 @@ static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op,
blit_op->src.roi.width = src_w;
if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) {
- tmp_v =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- MDP_MAX_X_SCALE_FACTOR +
- ((MDP_SCALE_Q_FACTOR *
- blit_op->dst.roi.height) %
- MDP_MAX_X_SCALE_FACTOR ? 1 : 0);
+ blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) {
+ tmp_v = (MDP_SCALE_Q_FACTOR *
+ blit_op->dst.roi.height) /
+ MDP_MAX_X_SCALE_FACTOR +
+ ((MDP_SCALE_Q_FACTOR *
+ blit_op->dst.roi.height) %
+ MDP_MAX_X_SCALE_FACTOR ? 1 : 0);
/* move x location as roi width gets bigger */
blit_op->src.roi.x -= tmp_v - blit_op->src.roi.width;
blit_op->src.roi.width = tmp_v;
} else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) {
- tmp_v =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- MDP_MIN_X_SCALE_FACTOR +
- ((MDP_SCALE_Q_FACTOR *
- blit_op->dst.roi.height) %
- MDP_MIN_X_SCALE_FACTOR ? 1 : 0);
-
+ blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) {
+ tmp_v = (MDP_SCALE_Q_FACTOR *
+ blit_op->dst.roi.height) /
+ MDP_MIN_X_SCALE_FACTOR +
+ ((MDP_SCALE_Q_FACTOR *
+ blit_op->dst.roi.height) %
+ MDP_MIN_X_SCALE_FACTOR ? 1 : 0);
/*
* we don't move x location for continuity of
* source image
@@ -993,6 +1176,7 @@ int mdp3_ppp_start_blit(struct msm_fb_data_type *mfd,
void mdp3_ppp_wait_for_fence(struct blit_req_list *req)
{
int i, ret = 0;
+ ATRACE_BEGIN(__func__);
/* buf sync */
for (i = 0; i < req->acq_fen_cnt; i++) {
ret = sync_fence_wait(req->acq_fen[i],
@@ -1004,7 +1188,7 @@ void mdp3_ppp_wait_for_fence(struct blit_req_list *req)
}
sync_fence_put(req->acq_fen[i]);
}
-
+ ATRACE_END(__func__);
if (ret < 0) {
while (i < req->acq_fen_cnt) {
sync_fence_put(req->acq_fen[i]);
@@ -1124,6 +1308,7 @@ void mdp3_ppp_req_pop(struct blit_req_queue *req_q)
void mdp3_free_fw_timer_func(unsigned long arg)
{
+ mdp3_res->solid_fill_vote_en = false;
schedule_work(&ppp_stat->free_bw_work);
}
@@ -1138,11 +1323,170 @@ static void mdp3_free_bw_wq_handler(struct work_struct *work)
mutex_unlock(&ppp_stat->config_ppp_mutex);
}
+static bool is_hw_workaround_needed(struct mdp_blit_req req)
+{
+ bool result = false;
+ bool is_bpp_4 = false;
+ uint32_t remainder = 0;
+ uint32_t bpp = ppp_get_bpp(req.dst.format, ppp_stat->mfd->fb_imgType);
+
+ /* MDP width split workaround */
+ remainder = (req.dst_rect.w) % 16;
+ is_bpp_4 = (bpp == 4) ? 1 : 0;
+ if ((is_bpp_4 && (remainder == 6 || remainder == 14)) &&
+ !(req.flags & MDP_SOLID_FILL))
+ result = true;
+
+ /* bg tile fetching HW workaround */
+ if (((req.alpha < MDP_ALPHA_NOP) ||
+ (req.transp_mask != MDP_TRANSP_NOP) ||
+ (req.src.format == MDP_ARGB_8888) ||
+ (req.src.format == MDP_BGRA_8888) ||
+ (req.src.format == MDP_RGBA_8888)) &&
+ (req.flags & MDP_ROT_90) && (req.dst_rect.w <= 16))
+ result = true;
+
+ return result;
+}
+
+static bool is_roi_equal(struct mdp_blit_req req0,
+ struct mdp_blit_req req1)
+{
+ bool result = false;
+ struct mdss_panel_info *panel_info = ppp_stat->mfd->panel_info;
+
+ /*
+ * Check req0 and req1 layer destination ROI and return true if
+ * they are equal.
+ */
+ if ((req0.dst_rect.x == req1.dst_rect.x) &&
+ (req0.dst_rect.y == req1.dst_rect.y) &&
+ (req0.dst_rect.w == req1.dst_rect.w) &&
+ (req0.dst_rect.h == req1.dst_rect.h))
+ result = true;
+ /*
+ * Layers are source cropped and cropped layer width and hight are
+ * same panel width and height
+ */
+ else if ((req0.dst_rect.w == req1.dst_rect.w) &&
+ (req0.dst_rect.h == req1.dst_rect.h) &&
+ (req0.dst_rect.w == panel_info->xres) &&
+ (req0.dst_rect.h == panel_info->yres))
+ result = true;
+
+ return result;
+}
+
+static bool is_scaling_needed(struct mdp_blit_req req)
+{
+ bool result = true;
+
+ /* Return true if layer need scaling else return false */
+ if ((req.src_rect.w == req.dst_rect.w) &&
+ (req.src_rect.h == req.dst_rect.h))
+ result = false;
+ return result;
+}
+
+static bool is_blit_optimization_possible(struct blit_req_list *req, int indx)
+{
+ int next = indx + 1;
+ bool status = false;
+ struct mdp3_img_data tmp_data;
+ bool dst_roi_equal = false;
+ bool hw_woraround_active = false;
+ struct mdp_blit_req bg_req;
+ struct mdp_blit_req fg_req;
+
+ if (!(mdp3_res->smart_blit_en)) {
+ pr_debug("Smart BLIT disabled from sysfs\n");
+ return status;
+ }
+ if (next < req->count) {
+ bg_req = req->req_list[indx];
+ fg_req = req->req_list[next];
+ hw_woraround_active = is_hw_workaround_needed(bg_req);
+ dst_roi_equal = is_roi_equal(bg_req, fg_req);
+ /*
+ * Check userspace Smart BLIT Flag for current and next
+ * request Flag for smart blit FG layer index If blit
+ * request at index "n" has MDP_SMART_BLIT flag set then
+ * it will be used as BG layer in smart blit
+ * and request at index "n+1" will be used as FG layer
+ */
+ if ((bg_req.flags & MDP_SMART_BLIT) &&
+ (!(fg_req.flags & MDP_SMART_BLIT)) &&
+ (!(hw_woraround_active)))
+ status = true;
+ /*
+ * Enable SMART blit between request 0(BG) & request 1(FG) when
+ * destination ROI of BG and FG layer are same,
+ * No scaling on BG layer
+ * No rotation on BG Layer.
+ * BG Layer color format is RGB and marked as MDP_IS_FG.
+ */
+ else if ((mdp3_res->smart_blit_en & SMART_BLIT_RGB_EN) &&
+ (indx == 0) && (dst_roi_equal) &&
+ (bg_req.flags & MDP_IS_FG) &&
+ (!(is_scaling_needed(bg_req))) &&
+ (!(bg_req.flags & (MDP_ROT_90))) &&
+ (check_if_rgb(bg_req.src.format)) &&
+ (!(hw_woraround_active))) {
+ status = true;
+ req->req_list[indx].flags |= MDP_SMART_BLIT;
+ pr_debug("Optimize RGB Blit for Req Indx %d\n", indx);
+ }
+ /*
+ * Swap BG and FG layer to enable SMART blit between request
+ * 0(BG) & request 1(FG) when destination ROI of BG and FG
+ * layer are same, No scaling on FG and BG layer
+ * No rotation on FG Layer. BG Layer color format is YUV
+ */
+ else if ((indx == 0) &&
+ (mdp3_res->smart_blit_en & SMART_BLIT_YUV_EN) &&
+ (!(fg_req.flags & (MDP_ROT_90))) && (dst_roi_equal) &&
+ (!(check_if_rgb(bg_req.src.format))) &&
+ (!(hw_woraround_active))) {
+ /*
+ * swap blit requests at index 0 and 1. YUV layer at
+ * index 0 is replaced with UI layer request present
+ * at index 1. Since UI layer will be in background
+ * set IS_FG flag and clear it from YUV layer flags
+ */
+ if (!(is_scaling_needed(req->req_list[next]))) {
+ if (bg_req.flags & MDP_IS_FG) {
+ req->req_list[indx].flags &=
+ ~MDP_IS_FG;
+ req->req_list[next].flags |= MDP_IS_FG;
+ }
+ bg_req = req->req_list[next];
+ req->req_list[next] = req->req_list[indx];
+ req->req_list[indx] = bg_req;
+
+ tmp_data = req->src_data[next];
+ req->src_data[next] = req->src_data[indx];
+ req->src_data[indx] = tmp_data;
+
+ tmp_data = req->dst_data[next];
+ req->dst_data[next] = req->dst_data[indx];
+ req->dst_data[indx] = tmp_data;
+ status = true;
+ req->req_list[indx].flags |= MDP_SMART_BLIT;
+ pr_debug("Optimize YUV Blit for Req Indx %d\n",
+ indx);
+ }
+ }
+ }
+ return status;
+}
+
static void mdp3_ppp_blit_wq_handler(struct work_struct *work)
{
struct msm_fb_data_type *mfd = ppp_stat->mfd;
struct blit_req_list *req;
int i, rc = 0;
+ bool smart_blit = false;
+ int smart_blit_fg_index = -1;
mutex_lock(&ppp_stat->config_ppp_mutex);
req = mdp3_ppp_next_req(&ppp_stat->req_q);
@@ -1176,7 +1520,15 @@ static void mdp3_ppp_blit_wq_handler(struct work_struct *work)
}
ppp_stat->bw_update = false;
}
+ ATRACE_BEGIN("mpd3_ppp_start");
for (i = 0; i < req->count; i++) {
+ smart_blit = is_blit_optimization_possible(req, i);
+ if (smart_blit)
+ /*
+ * Blit request index of FG layer in
+ * smart blit
+ */
+ smart_blit_fg_index = i + 1;
if (!(req->req_list[i].flags & MDP_NO_BLIT)) {
/* Do the actual blit. */
if (!rc) {
@@ -1185,10 +1537,23 @@ static void mdp3_ppp_blit_wq_handler(struct work_struct *work)
&req->src_data[i],
&req->dst_data[i]);
}
- mdp3_put_img(&req->src_data[i]);
- mdp3_put_img(&req->dst_data[i]);
+ /* Unmap blit source buffer */
+ if (smart_blit == false) {
+ mdp3_put_img(&req->src_data[i],
+ MDP3_CLIENT_PPP);
+ }
+ if (smart_blit_fg_index == i) {
+ /* Unmap smart blit BG buffer */
+ mdp3_put_img(&req->src_data[i - 1],
+ MDP3_CLIENT_PPP);
+ smart_blit_fg_index = -1;
+ }
+ mdp3_put_img(&req->dst_data[i],
+ MDP3_CLIENT_PPP);
+ smart_blit = false;
}
}
+ ATRACE_END("mdp3_ppp_start");
/* Signal to release fence */
mutex_lock(&ppp_stat->req_mutex);
mdp3_ppp_signal_timeline(req);
@@ -1257,7 +1622,7 @@ int mdp3_ppp_parse_req(void __user *p,
rc = mdp3_ppp_get_img(&req->req_list[i].dst,
&req->req_list[i], &req->dst_data[i]);
if (rc < 0 || req->dst_data[i].len == 0) {
- mdp3_put_img(&req->src_data[i]);
+ mdp3_put_img(&req->src_data[i], MDP3_CLIENT_PPP);
pr_err("mdp_ppp: couldn't retrieve dest img from mem\n");
goto parse_err_1;
}
@@ -1300,8 +1665,8 @@ parse_err_2:
put_unused_fd(req->cur_rel_fen_fd);
parse_err_1:
for (i--; i >= 0; i--) {
- mdp3_put_img(&req->src_data[i]);
- mdp3_put_img(&req->dst_data[i]);
+ mdp3_put_img(&req->src_data[i], MDP3_CLIENT_PPP);
+ mdp3_put_img(&req->dst_data[i], MDP3_CLIENT_PPP);
}
mdp3_ppp_deinit_buf_sync(req);
mutex_unlock(&ppp_stat->req_mutex);
diff --git a/drivers/video/fbdev/msm/mdp3_ppp.h b/drivers/video/fbdev/msm/mdp3_ppp.h
index b43807e62033..428906370a8c 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp.h
+++ b/drivers/video/fbdev/msm/mdp3_ppp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2007, 2013, 2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2007 Google Incorporated
*
* This software is licensed under the terms of the GNU General Public
@@ -23,6 +23,8 @@
#define PPP_BLUR_SCALE_MAX 128
#define PPP_LUT_MAX 256
+#define MDPOP_SMART_BLIT BIT(31) /* blit optimization flag */
+
/* MDP PPP Operations */
#define MDPOP_NOP 0
#define MDPOP_LR BIT(0) /* left to right flip */
@@ -51,6 +53,8 @@
#define PPP_OP_FLIP_UD BIT(11)
#define PPP_OP_BLEND_ON BIT(12)
#define PPP_OP_BLEND_CONSTANT_ALPHA BIT(14)
+#define PPP_OP_BLEND_BG_ALPHA BIT(13)
+#define PPP_OP_BLEND_EQ_REVERSE BIT(15)
#define PPP_OP_DITHER_EN BIT(16)
#define PPP_BLEND_CALPHA_TRNASP BIT(24)
@@ -298,6 +302,8 @@ struct ppp_resource {
u64 next_ab;
u64 next_ib;
u64 clk_rate;
+ u64 solid_fill_pixel;
+ u64 solid_fill_byte;
};
struct ppp_csc_table {
@@ -393,6 +399,8 @@ struct ppp_edge_rep {
int32_t luma_repeat_bottom;
};
+bool check_if_rgb(int color);
+
/* func for ppp register values */
uint32_t ppp_bpp(uint32_t type);
uint32_t ppp_src_config(uint32_t type);
diff --git a/drivers/video/fbdev/msm/mdp3_ppp_hwio.c b/drivers/video/fbdev/msm/mdp3_ppp_hwio.c
index 8c5d77121ea5..5ba3fbdb6238 100644
--- a/drivers/video/fbdev/msm/mdp3_ppp_hwio.c
+++ b/drivers/video/fbdev/msm/mdp3_ppp_hwio.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2012-2013 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2007, 2012-2013, 2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2007 Google Incorporated
*
* This software is licensed under the terms of the GNU General Public
@@ -33,6 +33,13 @@
#define PQF_PLUS_5_PLUS_2 (PQF_PLUS_5 + 2)
#define PQF_PLUS_5_MINUS_2 (PQF_PLUS_5 - 2)
+enum {
+ LAYER_FG = 0,
+ LAYER_BG,
+ LAYER_FB,
+ LAYER_MAX,
+};
+
static long long mdp_do_div(long long num, long long den)
{
do_div(num, den);
@@ -155,7 +162,7 @@ static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in,
delta = ((int64_t) (org) << PQF_PLUS_4) - Oreq;
init_phase_temp -= delta;
- /* limit to valid range before the left shift */
+ /* limit to valid range before left shift */
delta = (init_phase_temp & (1LL << 63)) ?
4 : -4;
delta <<= PQF_PLUS_4;
@@ -174,8 +181,9 @@ static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in,
/*
* RPA IMPLEMENTATION
*
- * init_phase needs to be calculated in all RPA_on cases
- * because it's a numerator, not a fixed point value.
+ * init_phase needs to be calculated in all RPA_on
+ * cases because it's a numerator, not a fixed
+ * point value.
*/
/* map (org - .5) into destination space */
@@ -195,8 +203,10 @@ static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in,
dim_out);
Osprime -= point5;
- /* then floor & decrement to calculate the required
- starting coordinate */
+ /*
+ * then floor & decrement to calculate the required
+ * starting coordinate
+ */
Oreq = (Osprime & int_mask) - one;
/* calculate initial phase */
@@ -210,7 +220,9 @@ static int mdp_calc_scale_params(uint32_t org, uint32_t dim_in,
while (abs((int)(init_phase_temp >> PQF_PLUS_4)) > 4)
init_phase_temp += delta;
- /* right shift to account for extra bits of precision */
+ /*
+ * right shift to account for extra bits of precision
+ */
init_phase = (int)(init_phase_temp >> 4);
}
}
@@ -299,9 +311,12 @@ static uint32_t conv_rgb2yuv(uint32_t input_pixel,
comp_C0 = temp;
/* matrix multiplication */
- temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] + comp_C2 * matrix[2];
- temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] + comp_C2 * matrix[5];
- temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] + comp_C2 * matrix[8];
+ temp1 = comp_C0 * matrix[0] + comp_C1 * matrix[1] +
+ comp_C2 * matrix[2];
+ temp2 = comp_C0 * matrix[3] + comp_C1 * matrix[4] +
+ comp_C2 * matrix[5];
+ temp3 = comp_C0 * matrix[6] + comp_C1 * matrix[7] +
+ comp_C2 * matrix[8];
comp_C0 = temp1 + 0x100;
comp_C1 = temp2 + 0x100;
@@ -362,27 +377,37 @@ bool check_if_rgb(int color)
return rgb;
}
-uint8_t *mdp_dst_adjust_rot_addr(struct ppp_blit_op *iBuf,
- uint8_t *addr, uint32_t bpp, uint32_t uv)
+uint8_t *mdp_adjust_rot_addr(struct ppp_blit_op *iBuf,
+ uint8_t *addr, uint32_t bpp, uint32_t uv, uint32_t layer)
{
- uint32_t dest_ystride = iBuf->dst.prop.width * bpp;
+ uint32_t ystride = 0;
uint32_t h_slice = 1;
-
- if (uv && ((iBuf->dst.color_fmt == MDP_Y_CBCR_H2V2) ||
- (iBuf->dst.color_fmt == MDP_Y_CRCB_H2V2)))
+ uint32_t roi_width = 0;
+ uint32_t roi_height = 0;
+ uint32_t color_fmt = 0;
+
+ if (layer == LAYER_BG) {
+ ystride = iBuf->bg.prop.width * bpp;
+ roi_width = iBuf->bg.roi.width;
+ roi_height = iBuf->bg.roi.height;
+ color_fmt = iBuf->bg.color_fmt;
+ } else {
+ ystride = iBuf->dst.prop.width * bpp;
+ roi_width = iBuf->dst.roi.width;
+ roi_height = iBuf->dst.roi.height;
+ color_fmt = iBuf->dst.color_fmt;
+ }
+ if (uv && ((color_fmt == MDP_Y_CBCR_H2V2) ||
+ (color_fmt == MDP_Y_CRCB_H2V2)))
h_slice = 2;
if (((iBuf->mdp_op & MDPOP_ROT90) == MDPOP_ROT90) ^
((iBuf->mdp_op & MDPOP_LR) == MDPOP_LR)) {
- addr +=
- (iBuf->dst.roi.width -
- MIN(16, iBuf->dst.roi.width)) * bpp;
+ addr += (roi_width - MIN(16, roi_width)) * bpp;
}
if ((iBuf->mdp_op & MDPOP_UD) == MDPOP_UD) {
- addr +=
- ((iBuf->dst.roi.height -
- MIN(16, iBuf->dst.roi.height))/h_slice) *
- dest_ystride;
+ addr += ((roi_height - MIN(16, roi_height))/h_slice) *
+ ystride;
}
return addr;
@@ -390,7 +415,7 @@ uint8_t *mdp_dst_adjust_rot_addr(struct ppp_blit_op *iBuf,
void mdp_adjust_start_addr(struct ppp_blit_op *blit_op,
struct ppp_img_desc *img, int v_slice,
- int h_slice, int layer)
+ int h_slice, uint32_t layer)
{
uint32_t bpp = ppp_bpp(img->color_fmt);
int x = img->roi.x;
@@ -403,8 +428,8 @@ void mdp_adjust_start_addr(struct ppp_blit_op *blit_op,
img->p0 += (x + y * ALIGN(width, 128)) * bpp;
else
img->p0 += (x + y * width) * bpp;
- if (layer != 0)
- img->p0 = mdp_dst_adjust_rot_addr(blit_op, img->p0, bpp, 0);
+ if (layer != LAYER_FG)
+ img->p0 = mdp_adjust_rot_addr(blit_op, img->p0, bpp, 0, layer);
if (img->p1) {
/*
@@ -421,9 +446,9 @@ void mdp_adjust_start_addr(struct ppp_blit_op *blit_op,
img->p1 += ((x / h_slice) * h_slice +
((y == 0) ? 0 : ((y + 1) / v_slice - 1) * width)) * bpp;
- if (layer != 0)
- img->p0 = mdp_dst_adjust_rot_addr(blit_op,
- img->p0, bpp, 0);
+ if (layer != LAYER_FG)
+ img->p0 = mdp_adjust_rot_addr(blit_op,
+ img->p0, bpp, 0, layer);
}
}
@@ -555,7 +580,7 @@ int config_ppp_out(struct ppp_img_desc *dst, uint32_t yuv2rgb)
return 0;
}
-int config_ppp_background(struct ppp_img_desc *bg)
+int config_ppp_background(struct ppp_img_desc *bg, uint32_t yuv2rgb)
{
uint32_t val;
@@ -573,7 +598,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, 0),
+ PPP_WRITEL(ppp_pack_pattern(bg->color_fmt, yuv2rgb),
MDP3_PPP_BG_UNPACK_PATTERN1);
return 0;
}
@@ -938,11 +963,16 @@ int config_ppp_scale(struct ppp_blit_op *blit_op, uint32_t *pppop_reg_ptr)
if ((dstW != src->roi.width) ||
(dstH != src->roi.height) || mdp_blur) {
- mdp_calc_scale_params(blit_op->src.roi.x,
+ /*
+ * Use source origin as 0 for computing initial
+ * phase and step size. Incorrect initial phase and
+ * step size value results in green line issue.
+ */
+ mdp_calc_scale_params(0,
blit_op->src.roi.width,
dstW, 1, &phase_init_x,
&phase_step_x);
- mdp_calc_scale_params(blit_op->src.roi.y,
+ mdp_calc_scale_params(0,
blit_op->src.roi.height,
dstH, 0, &phase_init_y,
&phase_step_y);
@@ -996,7 +1026,8 @@ int config_ppp_csc(int src_color, int dst_color, uint32_t *pppop_reg_ptr)
}
int config_ppp_blend(struct ppp_blit_op *blit_op,
- uint32_t *pppop_reg_ptr)
+ uint32_t *pppop_reg_ptr,
+ bool is_yuv_smart_blit, int smart_blit_bg_alpha)
{
struct ppp_csc_table *csc;
uint32_t alpha, trans_color;
@@ -1070,11 +1101,32 @@ int config_ppp_blend(struct ppp_blit_op *blit_op,
if (blit_op->mdp_op & MDPOP_TRANSP)
*pppop_reg_ptr |=
PPP_BLEND_CALPHA_TRNASP;
+ if (is_yuv_smart_blit) {
+ *pppop_reg_ptr |= PPP_OP_ROT_ON |
+ PPP_OP_BLEND_ON |
+ PPP_OP_BLEND_BG_ALPHA |
+ PPP_OP_BLEND_EQ_REVERSE;
+
+ if (smart_blit_bg_alpha < 0xFF)
+ bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL |
+ PPP_BLEND_BG_DSTPIXEL_ALPHA;
+ else
+ bg_alpha = PPP_BLEND_BG_USE_ALPHA_SEL |
+ PPP_BLEND_BG_DSTPIXEL_ALPHA |
+ PPP_BLEND_BG_CONSTANT_ALPHA;
+
+ bg_alpha |= smart_blit_bg_alpha << 24;
+ PPP_WRITEL(bg_alpha, MDP3_PPP_BLEND_BG_ALPHA_SEL);
+ } else {
PPP_WRITEL(0, MDP3_PPP_BLEND_BG_ALPHA_SEL);
}
+ }
if (*pppop_reg_ptr & PPP_OP_BLEND_ON) {
- config_ppp_background(&blit_op->bg);
+ if (is_yuv_smart_blit)
+ config_ppp_background(&blit_op->bg, 1);
+ else
+ config_ppp_background(&blit_op->bg, 0);
if (blit_op->dst.color_fmt == MDP_YCRYCB_H2V1) {
*pppop_reg_ptr |= PPP_OP_BG_CHROMA_H2V1;
@@ -1086,9 +1138,13 @@ int config_ppp_blend(struct ppp_blit_op *blit_op,
}
}
}
+ if (is_yuv_smart_blit) {
+ PPP_WRITEL(0, MDP3_PPP_BLEND_PARAM);
+ } else {
val = (alpha << MDP_BLEND_CONST_ALPHA);
val |= (trans_color & MDP_BLEND_TRASP_COL_MASK);
PPP_WRITEL(val, MDP3_PPP_BLEND_PARAM);
+ }
return 0;
}
@@ -1112,6 +1168,20 @@ int config_ppp_op_mode(struct ppp_blit_op *blit_op)
uint32_t ppp_operation_reg = 0;
int sv_slice, sh_slice;
int dv_slice, dh_slice;
+ static struct ppp_img_desc bg_img_param;
+ static int bg_alpha;
+ static int bg_mdp_ops;
+ bool is_yuv_smart_blit = false;
+
+ /*
+ * Detect YUV smart blit,
+ * Check cached BG image plane 0 address is not NILL and
+ * source color format is YUV than it is YUV smart blit
+ * mark is_yuv_smart_blit true.
+ */
+ if ((bg_img_param.p0) &&
+ (!(check_if_rgb(blit_op->src.color_fmt))))
+ is_yuv_smart_blit = true;
sv_slice = sh_slice = dv_slice = dh_slice = 1;
@@ -1188,19 +1258,83 @@ int config_ppp_op_mode(struct ppp_blit_op *blit_op)
blit_op->dst.p1 = NULL;
}
+ if ((bg_img_param.p0) && (!(blit_op->mdp_op & MDPOP_SMART_BLIT))) {
+ /*
+ * Use cached smart blit BG layer info in
+ * smart Blit FG request
+ */
+ blit_op->bg = bg_img_param;
+ if (check_if_rgb(blit_op->bg.color_fmt)) {
+ blit_op->bg.p1 = 0;
+ blit_op->bg.stride1 = 0;
+ }
+ memset(&bg_img_param, 0, sizeof(bg_img_param));
+ } else {
blit_op->bg = blit_op->dst;
+ }
+ /* Cache smart blit BG layer info */
+ if (blit_op->mdp_op & MDPOP_SMART_BLIT)
+ bg_img_param = blit_op->src;
+
/* Jumping from Y-Plane to Chroma Plane */
/* first pixel addr calculation */
- mdp_adjust_start_addr(blit_op, &blit_op->src, sv_slice, sh_slice, 0);
- mdp_adjust_start_addr(blit_op, &blit_op->bg, dv_slice, dh_slice, 1);
- mdp_adjust_start_addr(blit_op, &blit_op->dst, dv_slice, dh_slice, 2);
+ mdp_adjust_start_addr(blit_op, &blit_op->src, sv_slice,
+ sh_slice, LAYER_FG);
+ mdp_adjust_start_addr(blit_op, &blit_op->bg, dv_slice,
+ dh_slice, LAYER_BG);
+ mdp_adjust_start_addr(blit_op, &blit_op->dst, dv_slice,
+ dh_slice, LAYER_FB);
config_ppp_scale(blit_op, &ppp_operation_reg);
- config_ppp_blend(blit_op, &ppp_operation_reg);
+ config_ppp_blend(blit_op, &ppp_operation_reg, is_yuv_smart_blit,
+ bg_alpha);
config_ppp_src(&blit_op->src, yuv2rgb);
config_ppp_out(&blit_op->dst, yuv2rgb);
+
+ /* Cache Smart blit BG alpha adn MDP OP values */
+ if (blit_op->mdp_op & MDPOP_SMART_BLIT) {
+ bg_alpha = blit_op->blend.const_alpha;
+ bg_mdp_ops = blit_op->mdp_op;
+ } else {
+ bg_alpha = 0;
+ bg_mdp_ops = 0;
+ }
+ pr_debug("BLIT FG Param Fmt %d (x %d,y %d,w %d,h %d), ",
+ blit_op->src.color_fmt, blit_op->src.prop.x,
+ blit_op->src.prop.y, blit_op->src.prop.width,
+ blit_op->src.prop.height);
+ pr_debug("ROI(x %d,y %d,w %d, h %d) ",
+ blit_op->src.roi.x, blit_op->src.roi.y,
+ blit_op->src.roi.width, blit_op->src.roi.height);
+ pr_debug("Addr_P0 %p, Stride S0 %d Addr_P1 %p, Stride S1 %d\n",
+ blit_op->src.p0, blit_op->src.stride0,
+ blit_op->src.p1, blit_op->src.stride1);
+
+ if (blit_op->bg.p0 != blit_op->dst.p0) {
+ pr_debug("BLIT BG Param Fmt %d (x %d,y %d,w %d,h %d), ",
+ blit_op->bg.color_fmt, blit_op->bg.prop.x,
+ blit_op->bg.prop.y, blit_op->bg.prop.width,
+ blit_op->bg.prop.height);
+ pr_debug("ROI(x %d,y %d, w %d, h %d) ",
+ blit_op->bg.roi.x, blit_op->bg.roi.y,
+ blit_op->bg.roi.width, blit_op->bg.roi.height);
+ pr_debug("Addr %p, Stride S0 %d Addr_P1 %p, Stride S1 %d\n",
+ blit_op->bg.p0, blit_op->bg.stride0,
+ blit_op->bg.p1, blit_op->bg.stride1);
+ }
+ pr_debug("BLIT FB Param Fmt %d (x %d,y %d,w %d,h %d), ",
+ blit_op->dst.color_fmt, blit_op->dst.prop.x,
+ blit_op->dst.prop.y, blit_op->dst.prop.width,
+ blit_op->dst.prop.height);
+ pr_debug("ROI(x %d,y %d, w %d, h %d) ",
+ blit_op->dst.roi.x, blit_op->dst.roi.y,
+ blit_op->dst.roi.width, blit_op->dst.roi.height);
+ pr_debug("Addr %p, Stride S0 %d Addr_P1 %p, Stride S1 %d\n",
+ blit_op->dst.p0, blit_op->src.stride0,
+ blit_op->dst.p1, blit_op->dst.stride1);
+
PPP_WRITEL(ppp_operation_reg, MDP3_PPP_OP_MODE);
mb();
return 0;
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index 83fb479fe1c5..f92f3ae40d74 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -20,6 +20,7 @@
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/irqreturn.h>
+#include <linux/irqdomain.h>
#include <linux/mdss_io_util.h>
#include <linux/msm-bus.h>
@@ -54,9 +55,9 @@ enum mdss_iommu_domain_type {
enum mdss_bus_vote_type {
VOTE_INDEX_DISABLE,
- VOTE_INDEX_19_MHZ,
- VOTE_INDEX_40_MHZ,
- VOTE_INDEX_80_MHZ,
+ VOTE_INDEX_LOW,
+ VOTE_INDEX_MID,
+ VOTE_INDEX_HIGH,
VOTE_INDEX_MAX,
};
@@ -128,6 +129,7 @@ enum mdss_hw_index {
MDSS_HW_DSI1,
MDSS_HW_HDMI,
MDSS_HW_EDP,
+ MDSS_HW_MISC,
MDSS_MAX_HW_BLK
};
@@ -159,6 +161,7 @@ enum mdss_hw_quirk {
MDSS_QUIRK_DMA_BI_DIR,
MDSS_QUIRK_MIN_BUS_VOTE,
MDSS_QUIRK_FMT_PACK_PATTERN,
+ MDSS_QUIRK_NEED_SECURE_MAP,
MDSS_QUIRK_MAX,
};
@@ -316,6 +319,7 @@ struct mdss_data_type {
u32 default_ot_rd_limit;
u32 default_ot_wr_limit;
+ struct irq_domain *irq_domain;
u32 mdp_irq_mask;
u32 mdp_hist_irq_mask;
@@ -355,6 +359,7 @@ struct mdss_data_type {
u32 curr_bw_uc_idx;
u32 ao_bw_uc_idx; /* active only idx */
struct msm_bus_scale_pdata *bus_scale_table;
+ struct msm_bus_scale_pdata *reg_bus_scale_table;
u32 max_bw_low;
u32 max_bw_high;
u32 max_bw_per_pipe;
@@ -525,6 +530,7 @@ struct mdss_util_intf {
int (*bus_scale_set_quota)(int client, u64 ab_quota, u64 ib_quota);
int (*panel_intf_status)(u32 disp_num, u32 intf_type);
struct mdss_panel_cfg* (*panel_intf_type)(int intf_val);
+ int (*dyn_clk_gating_ctrl)(int enable);
};
struct mdss_util_intf *mdss_get_util_intf(void);
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 3bc5de64941f..e391a5aaa45d 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 1994 Martin Schaller
*
* 2001 - Documented with DocBook
@@ -445,8 +445,8 @@ static int __compat_async_position_update(struct fb_info *info,
update_pos.input_layer_cnt = update_pos32.input_layer_cnt;
layer_cnt = update_pos32.input_layer_cnt;
- if (!layer_cnt) {
- pr_err("no async layer to update\n");
+ if ((!layer_cnt) || (layer_cnt > MAX_LAYER_COUNT)) {
+ pr_err("invalid async layers :%d to update\n", layer_cnt);
return -EINVAL;
}
diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c
index 65874e5ab3b8..b14a83e863ab 100644
--- a/drivers/video/fbdev/msm/mdss_dba_utils.c
+++ b/drivers/video/fbdev/msm/mdss_dba_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, 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
@@ -300,6 +300,7 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
bool operands_present = false;
u32 no_of_operands, size, i;
u32 operands_offset = MAX_CEC_FRAME_SIZE - MAX_OPERAND_SIZE;
+ struct msm_hdmi_audio_edid_blk blk;
if (!udata) {
pr_err("Invalid data\n");
@@ -319,10 +320,14 @@ static void mdss_dba_utils_dba_cb(void *data, enum msm_dba_callback_event event)
ret = udata->ops.get_raw_edid(udata->dba_data,
udata->edid_buf_size, udata->edid_buf, 0);
- if (!ret)
+ if (!ret) {
hdmi_edid_parser(udata->edid_data);
- else
+ hdmi_edid_get_audio_blk(udata->edid_data, &blk);
+ udata->ops.set_audio_block(udata->dba_data,
+ sizeof(blk), &blk);
+ } else {
pr_err("failed to get edid%d\n", ret);
+ }
}
if (pluggable) {
@@ -676,13 +681,6 @@ void *mdss_dba_utils_init(struct mdss_dba_utils_init_data *uid)
udata->kobj = uid->kobj;
udata->pinfo = uid->pinfo;
- /* register display and audio switch devices */
- ret = mdss_dba_utils_init_switch_dev(udata, uid->fb_node);
- if (ret) {
- pr_err("switch dev registration failed\n");
- goto error;
- }
-
/* Initialize EDID feature */
edid_init_data.kobj = uid->kobj;
edid_init_data.ds_data.ds_registered = true;
@@ -738,10 +736,18 @@ void *mdss_dba_utils_init(struct mdss_dba_utils_init_data *uid)
* this explicit calls to bridge chip driver.
*/
if (!uid->pinfo->is_pluggable) {
- if (udata->ops.power_on)
+ if (udata->ops.power_on && !(uid->cont_splash_enabled))
udata->ops.power_on(udata->dba_data, true, 0);
if (udata->ops.check_hpd)
udata->ops.check_hpd(udata->dba_data, 0);
+ } else {
+ /* register display and audio switch devices */
+ ret = mdss_dba_utils_init_switch_dev(udata,
+ uid->fb_node);
+ if (ret) {
+ pr_err("switch dev registration failed\n");
+ goto error;
+ }
}
}
diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.h b/drivers/video/fbdev/msm/mdss_dba_utils.h
index cf43d2def7c3..70763af953f2 100644
--- a/drivers/video/fbdev/msm/mdss_dba_utils.h
+++ b/drivers/video/fbdev/msm/mdss_dba_utils.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, 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
@@ -24,6 +24,7 @@
* @chip_name: Name of the device registered with DBA
* @client_name: Name of the client registering with DBA
* @pinfo: Detailed panel information
+ * @cont_splash_enabled: Flag to check if cont splash was enabled on bridge
*
* This structure's instance is needed to be passed as parameter
* to register API to let the DBA utils module configure and
@@ -36,6 +37,7 @@ struct mdss_dba_utils_init_data {
char *chip_name;
char *client_name;
struct mdss_panel_info *pinfo;
+ bool cont_splash_enabled;
};
int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo);
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 9ba51bcc900a..82144ec653f9 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -181,7 +181,8 @@ static ssize_t panel_debug_base_reg_write(struct file *file,
if (mdata->debug_inf.debug_enable_clock)
mdata->debug_inf.debug_enable_clock(1);
- mdss_dsi_cmdlist_put(ctrl_pdata, &cmdreq);
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)
+ mdss_dsi_cmdlist_put(ctrl_pdata, &cmdreq);
if (mdata->debug_inf.debug_enable_clock)
mdata->debug_inf.debug_enable_clock(0);
@@ -994,7 +995,7 @@ static ssize_t mdss_debug_perf_bw_limit_write(struct file *file,
{
struct mdss_data_type *mdata = file->private_data;
char buf[32];
- u32 mode, val;
+ u32 mode = 0, val = 0;
u32 cnt;
struct mdss_max_bw_settings *temp_settings;
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 8cec9988f018..646f75653583 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -934,6 +934,9 @@ static int mdss_dsi_debugfs_init(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
}
pdata = &ctrl_pdata->panel_data;
+ if (!pdata)
+ return -EINVAL;
+
panel_info = pdata->panel_info;
rc = mdss_dsi_debugfs_setup(pdata, panel_info.debugfs_info->root);
if (rc) {
@@ -1129,6 +1132,8 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata, int power_state)
/* disable DSI phy */
mdss_dsi_phy_disable(ctrl_pdata);
}
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_DSI_ACTIVE;
+
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
@@ -1289,8 +1294,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
* to be restored to allow dcs command be
* sent to panel
*/
- mdss_dsi_read_hw_revision(ctrl_pdata);
- mdss_dsi_read_phy_revision(ctrl_pdata);
mdss_dsi_restore_intr_mask(ctrl_pdata);
pr_debug("%s: panel already on\n", __func__);
goto end;
@@ -1321,10 +1324,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
- /* Populate DSI Controller and PHY revision */
- mdss_dsi_read_hw_revision(ctrl_pdata);
- mdss_dsi_read_phy_revision(ctrl_pdata);
-
/*
* If ULPS during suspend feature is enabled, then DSI PHY was
* left on during suspend. In this case, we do not need to reset/init
@@ -1337,6 +1336,7 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
mdss_dsi_phy_init(ctrl_pdata);
mdss_dsi_ctrl_setup(ctrl_pdata);
}
+ ctrl_pdata->ctrl_state |= CTRL_STATE_DSI_ACTIVE;
/* DSI link clocks need to be on prior to ctrl sw reset */
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
@@ -1453,9 +1453,9 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
panel_data);
mipi = &pdata->panel_info.mipi;
- pr_debug("%s+: ctrl=%p ndx=%d cur_blank_state=%d ctrl_state=%x\n",
+ pr_debug("%s+: ctrl=%p ndx=%d cur_power_state=%d ctrl_state=%x\n",
__func__, ctrl_pdata, ctrl_pdata->ndx,
- pdata->panel_info.blank_state, ctrl_pdata->ctrl_state);
+ pdata->panel_info.panel_power_state, ctrl_pdata->ctrl_state);
mdss_dsi_pm_qos_update_request(DSI_DISABLE_PC_LATENCY);
@@ -1468,7 +1468,7 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
mdss_dsi_clk_ctrl(sctrl, sctrl->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON);
- if (pdata->panel_info.blank_state == MDSS_PANEL_BLANK_LOW_POWER) {
+ if (mdss_dsi_is_panel_on_lp(pdata)) {
pr_debug("%s: dsi_unblank with panel always on\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, false);
@@ -1486,7 +1486,6 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
}
ATRACE_END("dsi_panel_on");
}
- ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
}
if ((pdata->panel_info.type == MIPI_CMD_PANEL) &&
@@ -1496,6 +1495,8 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
}
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
+
error:
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF);
@@ -1702,7 +1703,7 @@ static void __mdss_dsi_dyn_refresh_config(
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
int reg_data = 0;
- u32 phy_rev = mdss_dsi_get_phy_revision(ctrl_pdata);
+ u32 phy_rev = ctrl_pdata->shared_data->phy_rev;
/* configure only for master control in split display */
if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) &&
@@ -1815,7 +1816,7 @@ static int __mdss_dsi_dfps_calc_clks(struct mdss_panel_data *pdata,
}
pinfo = &pdata->panel_info;
- phy_rev = mdss_dsi_get_phy_revision(ctrl_pdata);
+ phy_rev = ctrl_pdata->shared_data->phy_rev;
rc = mdss_dsi_clk_div_config
(&ctrl_pdata->panel_data.panel_info, new_fps);
@@ -2086,7 +2087,7 @@ static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps)
return -EINVAL;
}
- phy_rev = mdss_dsi_get_phy_revision(ctrl_pdata);
+ phy_rev = ctrl_pdata->shared_data->phy_rev;
pinfo = &pdata->panel_info;
/* get the fps configured in HW */
@@ -2260,12 +2261,19 @@ static void mdss_dsi_dba_work(struct work_struct *work)
memset(&utils_init_data, 0, sizeof(utils_init_data));
- utils_init_data.chip_name = "adv7533";
+ utils_init_data.chip_name = ctrl_pdata->bridge_name;
utils_init_data.client_name = "dsi";
- utils_init_data.instance_id = 0;
+ utils_init_data.instance_id = ctrl_pdata->bridge_index;
utils_init_data.fb_node = ctrl_pdata->fb_node;
utils_init_data.kobj = ctrl_pdata->kobj;
utils_init_data.pinfo = pinfo;
+ if (ctrl_pdata->mdss_util)
+ utils_init_data.cont_splash_enabled =
+ ctrl_pdata->mdss_util->panel_intf_status(
+ ctrl_pdata->panel_data.panel_info.pdest,
+ MDSS_PANEL_INTF_DSI) ? true : false;
+ else
+ utils_init_data.cont_splash_enabled = false;
pinfo->dba_data = mdss_dba_utils_init(&utils_init_data);
@@ -2339,6 +2347,15 @@ int mdss_dsi_register_recovery_handler(struct mdss_dsi_ctrl_pdata *ctrl,
return 0;
}
+static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct mdss_intf_recovery *mdp_callback)
+{
+ mutex_lock(&ctrl->mutex);
+ ctrl->mdp_callback = mdp_callback;
+ mutex_unlock(&ctrl->mutex);
+ return 0;
+}
+
static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev)
{
struct device_node *fb_node;
@@ -2408,8 +2425,6 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_clk_refresh(pdata,
ctrl_pdata->update_phy_timing);
- mdss_dsi_get_hw_revision(ctrl_pdata);
- mdss_dsi_get_phy_revision(ctrl_pdata);
rc = mdss_dsi_on(pdata);
mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode,
pdata);
@@ -2491,6 +2506,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_register_recovery_handler(ctrl_pdata,
(struct mdss_intf_recovery *)arg);
break;
+ case MDSS_EVENT_REGISTER_MDP_CALLBACK:
+ rc = mdss_dsi_register_mdp_callback(ctrl_pdata,
+ (struct mdss_intf_recovery *)arg);
+ break;
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
mode = (u32)(unsigned long) arg;
mdss_dsi_switch_mode(pdata, mode);
@@ -2862,7 +2881,7 @@ static int mdss_dsi_cont_splash_config(struct mdss_panel_info *pinfo,
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
void *clk_handle;
- int rc = 0, data;
+ int rc = 0;
if (pinfo->cont_splash_enabled) {
rc = mdss_dsi_panel_power_ctrl(&(ctrl_pdata->panel_data),
@@ -2873,7 +2892,8 @@ static int mdss_dsi_cont_splash_config(struct mdss_panel_info *pinfo,
}
if (ctrl_pdata->bklt_ctrl == BL_PWM)
mdss_dsi_panel_pwm_enable(ctrl_pdata);
- pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
+ ctrl_pdata->ctrl_state |= (CTRL_STATE_PANEL_INIT |
+ CTRL_STATE_MDP_ACTIVE | CTRL_STATE_DSI_ACTIVE);
if (ctrl_pdata->panel_data.panel_info.type == MIPI_CMD_PANEL)
clk_handle = ctrl_pdata->mdp_clk_handle;
else
@@ -2881,23 +2901,55 @@ static int mdss_dsi_cont_splash_config(struct mdss_panel_info *pinfo,
mdss_dsi_clk_ctrl(ctrl_pdata, clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON);
+ mdss_dsi_read_hw_revision(ctrl_pdata);
+ mdss_dsi_read_phy_revision(ctrl_pdata);
ctrl_pdata->is_phyreg_enabled = 1;
- mdss_dsi_get_hw_revision(ctrl_pdata);
- if ((ctrl_pdata->shared_data->hw_rev >= MDSS_DSI_HW_REV_103)
- && (pinfo->type == MIPI_CMD_PANEL)) {
- data = MIPI_INP(ctrl_pdata->ctrl_base + 0x1b8);
- if (data & BIT(16))
- ctrl_pdata->burst_mode_enabled = true;
- }
- ctrl_pdata->ctrl_state |=
- (CTRL_STATE_PANEL_INIT | CTRL_STATE_MDP_ACTIVE);
+ if (pinfo->type == MIPI_CMD_PANEL)
+ mdss_dsi_set_burst_mode(ctrl_pdata);
} else {
+ /* Turn on the clocks to read the DSI and PHY revision */
+ mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
+ mdss_dsi_read_hw_revision(ctrl_pdata);
+ mdss_dsi_read_phy_revision(ctrl_pdata);
+ mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
+ MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
pinfo->panel_power_state = MDSS_PANEL_POWER_OFF;
}
return rc;
}
+static int mdss_dsi_get_bridge_chip_params(struct mdss_panel_info *pinfo,
+ struct mdss_dsi_ctrl_pdata *ctrl_pdata,
+ struct platform_device *pdev)
+{
+ int rc = 0;
+ u32 temp_val = 0;
+
+ if (!ctrl_pdata || !pdev || !pinfo) {
+ pr_err("%s: Invalid Params ctrl_pdata=%p, pdev=%p\n", __func__,
+ ctrl_pdata, pdev);
+ rc = -EINVAL;
+ goto end;
+ }
+
+ if (pinfo->is_dba_panel) {
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,bridge-index", &temp_val);
+ if (rc) {
+ pr_err("%s:%d Unable to read qcom,bridge-index, ret=%d\n",
+ __func__, __LINE__, rc);
+ goto end;
+ }
+ pr_debug("%s: DT property %s is %X\n", __func__,
+ "qcom,bridge-index", temp_val);
+ ctrl_pdata->bridge_index = temp_val;
+ }
+end:
+ return rc;
+}
+
static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -3032,6 +3084,12 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
}
+ rc = mdss_dsi_get_bridge_chip_params(pinfo, ctrl_pdata, pdev);
+ if (rc) {
+ pr_err("%s: Failed to get bridge params\n", __func__);
+ goto error_shadow_clk_deinit;
+ }
+
ctrl_pdata->workq = create_workqueue("mdss_dsi_dba");
if (!ctrl_pdata->workq) {
pr_err("%s: Error creating workqueue\n", __func__);
@@ -3041,7 +3099,9 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&ctrl_pdata->dba_work, mdss_dsi_dba_work);
- pr_debug("%s: Dsi Ctrl->%d initialized\n", __func__, index);
+ pr_info("%s: Dsi Ctrl->%d initialized, DSI rev:0x%x, PHY rev:0x%x\n",
+ __func__, index, ctrl_pdata->shared_data->hw_rev,
+ ctrl_pdata->shared_data->phy_rev);
if (index == 0)
ctrl_pdata->shared_data->dsi0_active = true;
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index f3f708448ebd..1606f0c72a13 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -135,9 +135,19 @@ enum dsi_pm_type {
DSI_MAX_PM
};
+/*
+ * DSI controller states.
+ * CTRL_STATE_UNKNOWN - Unknown state of DSI controller.
+ * CTRL_STATE_PANEL_INIT - State specifies that the panel is initialized.
+ * CTRL_STATE_MDP_ACTIVE - State specifies that MDP is ready to send
+ * data to DSI.
+ * CTRL_STATE_DSI_ACTIVE - State specifies that DSI controller/PHY is
+ * initialized.
+ */
#define CTRL_STATE_UNKNOWN 0x00
#define CTRL_STATE_PANEL_INIT BIT(0)
#define CTRL_STATE_MDP_ACTIVE BIT(1)
+#define CTRL_STATE_DSI_ACTIVE BIT(2)
#define DSI_NON_BURST_SYNCH_PULSE 0
#define DSI_NON_BURST_SYNCH_EVENT 1
@@ -376,6 +386,7 @@ struct dsi_err_container {
#define MDSS_DSI_COMMAND_COMPRESSION_MODE_CTRL 0x02a8
#define MDSS_DSI_COMMAND_COMPRESSION_MODE_CTRL2 0x02ac
#define MDSS_DSI_COMMAND_COMPRESSION_MODE_CTRL3 0x02b0
+#define MSM_DBA_CHIP_NAME_MAX_LEN 20
struct mdss_dsi_ctrl_pdata {
int ndx; /* panel_num */
@@ -445,6 +456,7 @@ struct mdss_dsi_ctrl_pdata {
u32 dsi_irq_mask;
struct mdss_hw *dsi_hw;
struct mdss_intf_recovery *recovery;
+ struct mdss_intf_recovery *mdp_callback;
struct dsi_panel_cmds on_cmds;
struct dsi_panel_cmds post_dms_on_cmds;
@@ -502,6 +514,7 @@ struct mdss_dsi_ctrl_pdata {
bool cmd_cfg_restore;
bool do_unicast;
+ bool idle_enabled;
int horizontal_idle_cnt;
struct panel_horizontal_idle *line_idle;
struct mdss_util_intf *mdss_util;
@@ -516,16 +529,20 @@ struct mdss_dsi_ctrl_pdata {
struct dsi_err_container err_cont;
-
- bool ds_registered;
-
struct kobject *kobj;
int fb_node;
+ /* DBA data */
struct workqueue_struct *workq;
struct delayed_work dba_work;
+ char bridge_name[MSM_DBA_CHIP_NAME_MAX_LEN];
+ uint32_t bridge_index;
+ bool ds_registered;
+
bool timing_db_mode;
bool update_phy_timing; /* flag to recalculate PHY timings */
+
+ bool phy_power_off;
};
struct dsi_status_data {
@@ -552,7 +569,7 @@ void mdss_dsi_cmd_mode_ctrl(int enable);
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);
+bool mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl);
int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void *clk_handle,
enum mdss_dsi_clk_type clk_type, enum mdss_dsi_clk_state clk_state);
void mdss_dsi_clk_req(struct mdss_dsi_ctrl_pdata *ctrl,
@@ -614,9 +631,8 @@ 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);
void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl, bool print_en);
+bool mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl, bool print_en);
void mdss_dsi_lp_cd_rx(struct mdss_dsi_ctrl_pdata *ctrl);
-void mdss_dsi_get_hw_revision(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_read_phy_revision(struct mdss_dsi_ctrl_pdata *ctrl);
int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0,
char cmd1, void (*fxn)(int), char *rbuf, int len);
@@ -639,6 +655,7 @@ void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl,
void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsc_desc *dsc);
void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl);
static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module)
{
diff --git a/drivers/video/fbdev/msm/mdss_dsi_clk.c b/drivers/video/fbdev/msm/mdss_dsi_clk.c
index 0142ba8a5c5c..727d6707444c 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_clk.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_clk.c
@@ -99,7 +99,7 @@ static int dsi_core_clk_start(struct dsi_core_clks *c_clks)
}
}
- rc = mdss_update_reg_bus_vote(mngr->reg_bus_clt, VOTE_INDEX_19_MHZ);
+ rc = mdss_update_reg_bus_vote(mngr->reg_bus_clt, VOTE_INDEX_LOW);
if (rc) {
pr_err("failed to vote for reg bus\n");
goto disable_mmss_misc_clk;
@@ -876,7 +876,7 @@ void *mdss_dsi_clk_init(struct mdss_dsi_clk_info *info)
mngr->post_clkoff_cb = info->post_clkoff_cb;
mngr->priv_data = info->priv_data;
mngr->reg_bus_clt = mdss_reg_bus_vote_client_create(info->name);
- if (IS_ERR_OR_NULL(mngr->reg_bus_clt)) {
+ if (IS_ERR(mngr->reg_bus_clt)) {
pr_err("Unable to get handle for reg bus vote\n");
kfree(mngr);
mngr = ERR_PTR(-EINVAL);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 940b4b9db95e..665e3d03110f 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -277,23 +277,11 @@ void mdss_dsi_cmd_test_pattern(struct mdss_dsi_ctrl_pdata *ctrl)
void mdss_dsi_read_hw_revision(struct mdss_dsi_ctrl_pdata *ctrl)
{
- /* clock must be on */
- ctrl->shared_data->hw_rev = MIPI_INP(ctrl->ctrl_base);
-}
-
-void mdss_dsi_get_hw_revision(struct mdss_dsi_ctrl_pdata *ctrl)
-{
if (ctrl->shared_data->hw_rev)
return;
- mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle, MDSS_DSI_CORE_CLK,
- MDSS_DSI_CLK_ON);
+ /* clock must be on */
ctrl->shared_data->hw_rev = MIPI_INP(ctrl->ctrl_base);
- mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle, MDSS_DSI_CORE_CLK,
- MDSS_DSI_CLK_OFF);
-
- pr_debug("%s: ndx=%d hw_rev=%x\n", __func__,
- ctrl->ndx, ctrl->shared_data->hw_rev);
}
void mdss_dsi_read_phy_revision(struct mdss_dsi_ctrl_pdata *ctrl)
@@ -1243,6 +1231,32 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl, struct dsc_desc *dsc)
MIPI_OUTP((ctrl->ctrl_base) + offset, data);
}
+void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ u32 data;
+
+ if (ctrl->shared_data->hw_rev < MDSS_DSI_HW_REV_103)
+ return;
+
+ data = MIPI_INP(ctrl->ctrl_base + 0x1b8);
+
+ /*
+ * idle and burst mode are mutually exclusive features,
+ * so disable burst mode if idle has been configured for
+ * the panel, otherwise enable the feature.
+ */
+ if (ctrl->idle_enabled)
+ data &= ~BIT(16); /* disable burst mode */
+ else
+ data |= BIT(16); /* enable burst mode */
+
+ ctrl->burst_mode_enabled = !ctrl->idle_enabled;
+
+ MIPI_OUTP((ctrl->ctrl_base + 0x1b8), data);
+ pr_debug("%s: burst=%d\n", __func__, ctrl->burst_mode_enabled);
+
+}
+
static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata)
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
@@ -1353,13 +1367,7 @@ static void mdss_dsi_mode_setup(struct mdss_panel_data *pdata)
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x2b4, data);
}
- /* Enable frame transfer in burst mode */
- if (ctrl_pdata->shared_data->hw_rev >= MDSS_DSI_HW_REV_103) {
- data = MIPI_INP(ctrl_pdata->ctrl_base + 0x1b8);
- data = data | BIT(16);
- MIPI_OUTP((ctrl_pdata->ctrl_base + 0x1b8), data);
- ctrl_pdata->burst_mode_enabled = 1;
- }
+ mdss_dsi_set_burst_mode(ctrl_pdata);
/* DSI_COMMAND_MODE_MDP_STREAM_CTRL */
MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x60, stream_ctrl);
@@ -1488,8 +1496,6 @@ static int mdss_dsi_cmd_dma_tpg_tx(struct mdss_dsi_ctrl_pdata *ctrl,
return -EINVAL;
}
- mdss_dsi_get_hw_revision(ctrl);
-
if (ctrl->shared_data->hw_rev < MDSS_DSI_HW_REV_103) {
pr_err("CMD DMA TPG not supported for this DSI version\n");
return -EINVAL;
@@ -2228,7 +2234,7 @@ int mdss_dsi_en_wait4dynamic_done(struct mdss_dsi_ctrl_pdata *ctrl)
unsigned long flag;
u32 data;
int rc = 0;
- struct mdss_dsi_ctrl_pdata *sctrl_pdata;
+ struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL;
/* DSI_INTL_CTRL */
data = MIPI_INP((ctrl->ctrl_base) + 0x0110);
@@ -2250,7 +2256,12 @@ int mdss_dsi_en_wait4dynamic_done(struct mdss_dsi_ctrl_pdata *ctrl)
MIPI_OUTP((ctrl->ctrl_base) + DSI_DYNAMIC_REFRESH_CTRL,
(BIT(13) | BIT(8) | BIT(0)));
- sctrl_pdata = mdss_dsi_get_ctrl_clk_slave();
+ /*
+ * Configure DYNAMIC_REFRESH_CTRL for second controller only
+ * for split DSI cases.
+ */
+ if (mdss_dsi_is_ctrl_clk_master(ctrl))
+ sctrl_pdata = mdss_dsi_get_ctrl_clk_slave();
if (sctrl_pdata)
MIPI_OUTP((sctrl_pdata->ctrl_base) + DSI_DYNAMIC_REFRESH_CTRL,
@@ -2468,6 +2479,49 @@ int mdss_dsi_cmdlist_rx(struct mdss_dsi_ctrl_pdata *ctrl,
return len;
}
+static inline bool mdss_dsi_delay_cmd(struct mdss_dsi_ctrl_pdata *ctrl,
+ bool from_mdp)
+{
+ unsigned long flags;
+ bool mdp_busy = false;
+ bool need_wait = false;
+
+ if (!ctrl->mdp_callback)
+ goto exit;
+
+ /* delay only for split dsi, cmd mode and burst mode enabled cases */
+ if (!mdss_dsi_is_hw_config_split(ctrl->shared_data) ||
+ !(ctrl->panel_mode == DSI_CMD_MODE) ||
+ !ctrl->burst_mode_enabled)
+ goto exit;
+
+ /* delay only if cmd is not from mdp and panel has been initialized */
+ if (from_mdp || !(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT))
+ goto exit;
+
+ /* if broadcast enabled, apply delay only if this is the ctrl trigger */
+ if (mdss_dsi_sync_wait_enable(ctrl) &&
+ !mdss_dsi_sync_wait_trigger(ctrl))
+ goto exit;
+
+ spin_lock_irqsave(&ctrl->mdp_lock, flags);
+ if (ctrl->mdp_busy == true)
+ mdp_busy = true;
+ spin_unlock_irqrestore(&ctrl->mdp_lock, flags);
+
+ /*
+ * apply delay only if:
+ * mdp_busy bool is set - kickoff is being scheduled by sw
+ * MDP_BUSY bit is not set - transfer is not on-going in hw yet
+ */
+ if (mdp_busy && !(MIPI_INP(ctrl->ctrl_base + 0x008) & BIT(2)))
+ need_wait = true;
+
+exit:
+ MDSS_XLOG(need_wait, from_mdp, mdp_busy);
+ return need_wait;
+}
+
int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
{
struct dcs_cmd_req *req;
@@ -2479,9 +2533,6 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
bool hs_req = false;
bool cmd_mutex_acquired = false;
- if (mdss_get_sd_client_cnt())
- return -EPERM;
-
if (from_mdp) { /* from mdp kickoff */
if (!ctrl->burst_mode_enabled) {
mutex_lock(&ctrl->cmd_mutex);
@@ -2509,7 +2560,20 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
mdss_dsi_cmd_mdp_busy(ctrl);
}
- mdss_dsi_get_hw_revision(ctrl);
+ /*
+ * if secure display session is enabled
+ * and DSI controller version is above 1.3.0,
+ * then send DSI commands using TPG FIFO.
+ */
+ if (mdss_get_sd_client_cnt() && req) {
+ if (ctrl->shared_data->hw_rev >= MDSS_DSI_HW_REV_103) {
+ req->flags |= CMD_REQ_DMA_TPG;
+ } else {
+ if (cmd_mutex_acquired)
+ mutex_unlock(&ctrl->cmd_mutex);
+ return -EPERM;
+ }
+ }
/* For DSI versions less than 1.3.0, CMD DMA TPG is not supported */
if (req && (ctrl->shared_data->hw_rev < MDSS_DSI_HW_REV_103))
@@ -2572,6 +2636,17 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle, MDSS_DSI_ALL_CLKS,
MDSS_DSI_CLK_ON);
+ /*
+ * In ping pong split cases, check if we need to apply a
+ * delay for any commands that are not coming from
+ * mdp path
+ */
+ mutex_lock(&ctrl->mutex);
+ if (mdss_dsi_delay_cmd(ctrl, from_mdp))
+ ctrl->mdp_callback->fxn(ctrl->mdp_callback->data,
+ MDP_INTF_CALLBACK_DSI_WAIT);
+ mutex_unlock(&ctrl->mutex);
+
if (req->flags & CMD_REQ_HS_MODE)
mdss_dsi_set_tx_power_mode(0, &ctrl->panel_data);
@@ -2588,10 +2663,10 @@ int mdss_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
ctrl->mdss_util->iommu_ctrl(0);
(void)mdss_dsi_bus_bandwidth_vote(ctrl->shared_data, false);
- mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle, MDSS_DSI_ALL_CLKS,
- MDSS_DSI_CLK_OFF);
}
+ mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle, MDSS_DSI_ALL_CLKS,
+ MDSS_DSI_CLK_OFF);
need_lock:
MDSS_XLOG(ctrl->ndx, from_mdp, ctrl->mdp_busy, current->pid,
@@ -2795,10 +2870,11 @@ static int dsi_event_thread(void *data)
return 0;
}
-void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl)
+bool mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 status;
unsigned char *base;
+ bool ret = false;
base = ctrl->ctrl_base;
@@ -2816,16 +2892,20 @@ void mdss_dsi_ack_err_status(struct mdss_dsi_ctrl_pdata *ctrl)
*/
if (ctrl->panel_data.panel_info.esd_check_enabled &&
(ctrl->status_mode == ESD_BTA) && (status & 0x1008000))
- return;
+ return false;
pr_err("%s: status=%x\n", __func__, status);
+ ret = true;
}
+
+ return ret;
}
-void mdss_dsi_timeout_status(struct mdss_dsi_ctrl_pdata *ctrl)
+static bool mdss_dsi_timeout_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 status;
unsigned char *base;
+ bool ret = false;
base = ctrl->ctrl_base;
@@ -2836,13 +2916,17 @@ void mdss_dsi_timeout_status(struct mdss_dsi_ctrl_pdata *ctrl)
if (status & 0x0110)
dsi_send_events(ctrl, DSI_EV_LP_RX_TIMEOUT, 0);
pr_err("%s: status=%x\n", __func__, status);
+ ret = true;
}
+
+ return ret;
}
-void mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl, bool print_en)
+bool mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl, bool print_en)
{
u32 status;
unsigned char *base;
+ bool ret = false;
base = ctrl->ctrl_base;
@@ -2853,13 +2937,17 @@ void mdss_dsi_dln0_phy_err(struct mdss_dsi_ctrl_pdata *ctrl, bool print_en)
if (print_en)
pr_err("%s: status=%x\n", __func__, status);
ctrl->err_cont.phy_err_cnt++;
+ ret = true;
}
+
+ return ret;
}
-void mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
+static bool mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 status, isr;
unsigned char *base;
+ bool ret = false;
base = ctrl->ctrl_base;
@@ -2891,13 +2979,17 @@ void mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
if (status & 0x11110000) /* DLN_FIFO_EMPTY */
dsi_send_events(ctrl, DSI_EV_DSI_FIFO_EMPTY, 0);
ctrl->err_cont.fifo_err_cnt++;
+ ret = true;
}
+
+ return ret;
}
-void mdss_dsi_status(struct mdss_dsi_ctrl_pdata *ctrl)
+static bool mdss_dsi_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 status;
unsigned char *base;
+ bool ret = false;
base = ctrl->ctrl_base;
@@ -2906,13 +2998,17 @@ void mdss_dsi_status(struct mdss_dsi_ctrl_pdata *ctrl)
if (status & 0x80000000) { /* INTERLEAVE_OP_CONTENTION */
MIPI_OUTP(base + 0x0008, status);
pr_err("%s: status=%x\n", __func__, status);
+ ret = true;
}
+
+ return ret;
}
-void mdss_dsi_clk_status(struct mdss_dsi_ctrl_pdata *ctrl)
+static bool mdss_dsi_clk_status(struct mdss_dsi_ctrl_pdata *ctrl)
{
u32 status;
unsigned char *base;
+ bool ret = false;
base = ctrl->ctrl_base;
status = MIPI_INP(base + 0x0120);/* DSI_CLK_STATUS */
@@ -2921,7 +3017,10 @@ void mdss_dsi_clk_status(struct mdss_dsi_ctrl_pdata *ctrl)
MIPI_OUTP(base + 0x0120, status);
dsi_send_events(ctrl, DSI_EV_PLL_UNLOCKED, 0);
pr_err("%s: status=%x\n", __func__, status);
+ ret = true;
}
+
+ return ret;
}
static void __dsi_error_counter(struct dsi_err_container *err_container)
@@ -2950,18 +3049,27 @@ static void __dsi_error_counter(struct dsi_err_container *err_container)
void mdss_dsi_error(struct mdss_dsi_ctrl_pdata *ctrl)
{
- u32 intr;
+ u32 intr, mask;
+ bool err_handled = false;
+
+ /* Ignore the interrupt if the error intr mask is not set */
+ mask = MIPI_INP(ctrl->ctrl_base + 0x0110);
+ if (!(mask & DSI_INTR_ERROR_MASK)) {
+ pr_debug("%s: Ignore interrupt as error mask not set, 0x%x\n",
+ __func__, mask);
+ return;
+ }
/* disable dsi error interrupt */
mdss_dsi_err_intr_ctrl(ctrl, DSI_INTR_ERROR_MASK, 0);
/* DSI_ERR_INT_MASK0 */
- mdss_dsi_clk_status(ctrl); /* Mask0, 0x10000000 */
- mdss_dsi_fifo_status(ctrl); /* mask0, 0x133d00 */
- mdss_dsi_ack_err_status(ctrl); /* mask0, 0x01f */
- mdss_dsi_timeout_status(ctrl); /* mask0, 0x0e0 */
- mdss_dsi_status(ctrl); /* mask0, 0xc0100 */
- mdss_dsi_dln0_phy_err(ctrl, true); /* mask0, 0x3e00000 */
+ err_handled |= mdss_dsi_clk_status(ctrl); /* Mask0, 0x10000000 */
+ err_handled |= mdss_dsi_fifo_status(ctrl); /* mask0, 0x133d00 */
+ err_handled |= mdss_dsi_ack_err_status(ctrl); /* mask0, 0x01f */
+ err_handled |= mdss_dsi_timeout_status(ctrl); /* mask0, 0x0e0 */
+ err_handled |= mdss_dsi_status(ctrl); /* mask0, 0xc0100 */
+ err_handled |= mdss_dsi_dln0_phy_err(ctrl, true);/* mask0, 0x3e00000 */
/* clear dsi error interrupt */
intr = MIPI_INP(ctrl->ctrl_base + 0x0110);
@@ -2969,7 +3077,9 @@ void mdss_dsi_error(struct mdss_dsi_ctrl_pdata *ctrl)
intr |= DSI_INTR_ERROR;
MIPI_OUTP(ctrl->ctrl_base + 0x0110, intr);
- __dsi_error_counter(&ctrl->err_cont);
+ if (err_handled)
+ __dsi_error_counter(&ctrl->err_cont);
+
dsi_send_events(ctrl, DSI_EV_MDP_BUSY_RELEASE, 0);
}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 9108bee02445..b6db0c2543af 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -731,10 +731,9 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata)
if (pinfo->compression_mode == COMPRESSION_DSC)
mdss_dsi_panel_dsc_pps_send(ctrl, pinfo);
- if (ctrl->ds_registered && pinfo->is_pluggable)
+ if (ctrl->ds_registered)
mdss_dba_utils_video_on(pinfo->dba_data, pinfo);
end:
- pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
pr_debug("%s:-\n", __func__);
return ret;
}
@@ -808,7 +807,6 @@ static int mdss_dsi_panel_off(struct mdss_panel_data *pdata)
}
end:
- pinfo->blank_state = MDSS_PANEL_BLANK_BLANK;
pr_debug("%s:-\n", __func__);
return 0;
}
@@ -832,10 +830,6 @@ static int mdss_dsi_panel_low_power_config(struct mdss_panel_data *pdata,
enable);
/* Any panel specific low power commands/config */
- if (enable)
- pinfo->blank_state = MDSS_PANEL_BLANK_LOW_POWER;
- else
- pinfo->blank_state = MDSS_PANEL_BLANK_UNBLANK;
pr_debug("%s:-\n", __func__);
return 0;
@@ -1331,6 +1325,7 @@ static void mdss_panel_parse_te_params(struct device_node *np,
rc = of_property_read_u32
(np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp);
te->rd_ptr_irq = (!rc ? tmp : timing->yres + 1);
+ te->wr_ptr_irq = 0;
}
@@ -1789,6 +1784,13 @@ static void mdss_dsi_parse_panel_horizintal_line_idle(struct device_node *np,
ctrl->horizontal_idle_cnt++;
}
+ /*
+ * idle is enabled for this controller, this will be used to
+ * enable/disable burst mode since both features are mutually
+ * exclusive.
+ */
+ ctrl->idle_enabled = true;
+
pr_debug("%s: horizontal_idle_cnt=%d\n", __func__,
ctrl->horizontal_idle_cnt);
}
@@ -1997,7 +1999,7 @@ static int mdss_dsi_panel_timing_from_dt(struct device_node *np,
const char *data;
struct mdss_dsi_ctrl_pdata *ctrl_pdata;
struct mdss_panel_info *pinfo;
- bool phy_timings_present;
+ bool phy_timings_present = false;
pinfo = &panel_data->panel_info;
@@ -2211,9 +2213,10 @@ static int mdss_panel_parse_dt(struct device_node *np,
struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
u32 tmp;
- int rc;
+ int rc, len = 0;
const char *data;
static const char *pdest;
+ const char *bridge_chip_name;
struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);
if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data))
@@ -2420,6 +2423,19 @@ static int mdss_panel_parse_dt(struct device_node *np,
pinfo->is_dba_panel = of_property_read_bool(np,
"qcom,dba-panel");
+ if (pinfo->is_dba_panel) {
+ bridge_chip_name = of_get_property(np,
+ "qcom,bridge-name", &len);
+ if (!bridge_chip_name || len <= 0) {
+ pr_err("%s:%d Unable to read qcom,bridge_name, data=%p,len=%d\n",
+ __func__, __LINE__, bridge_chip_name, len);
+ rc = -EINVAL;
+ goto error;
+ }
+ strlcpy(ctrl_pdata->bridge_name, bridge_chip_name,
+ MSM_DBA_CHIP_NAME_MAX_LEN);
+ }
+
return 0;
error:
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index 221b286b46d9..858465e6df78 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -1091,6 +1091,7 @@ static int mdss_fb_probe(struct platform_device *pdev)
mfd->pdev = pdev;
+ mfd->split_fb_left = mfd->split_fb_right = 0;
mfd->split_mode = MDP_SPLIT_MODE_NONE;
if (pdata->panel_info.is_split_display) {
struct mdss_panel_data *pnext = pdata->next;
@@ -1961,7 +1962,7 @@ void mdss_fb_free_fb_ion_memory(struct msm_fb_data_type *mfd)
int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, size_t fb_size)
{
- int rc;
+ int rc = 0;
void *vaddr;
int domain;
@@ -3907,8 +3908,8 @@ static int mdss_fb_async_position_update_ioctl(struct fb_info *info,
input_layer_list = update_pos.input_layers;
layer_cnt = update_pos.input_layer_cnt;
- if (!layer_cnt) {
- pr_err("no async layers to update\n");
+ if ((!layer_cnt) || (layer_cnt > MAX_LAYER_COUNT)) {
+ pr_err("invalid async layers :%d to update\n", layer_cnt);
return -EINVAL;
}
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index 954f11f729bd..67d42628597d 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -56,7 +56,6 @@
#define MDP_PP_AD_BL_LINEAR 0x0
#define MDP_PP_AD_BL_LINEAR_INV 0x1
-#define MAX_LAYER_COUNT 0xC
/**
* enum mdp_notify_event - Different frame events to indicate frame update state
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_audio.c b/drivers/video/fbdev/msm/mdss_hdmi_audio.c
new file mode 100644
index 000000000000..d949a86a8f5d
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.c
@@ -0,0 +1,525 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/iopoll.h>
+#include <linux/types.h>
+#include <linux/switch.h>
+#include <linux/gcd.h>
+
+#include "mdss_hdmi_audio.h"
+#include "mdss_hdmi_util.h"
+
+#define HDMI_AUDIO_INFO_FRAME_PACKET_HEADER 0x84
+#define HDMI_AUDIO_INFO_FRAME_PACKET_VERSION 0x1
+#define HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH 0x0A
+
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_MHZ_TO_HZ 1000000
+#define HDMI_ACR_N_MULTIPLIER 128
+#define DEFAULT_AUDIO_SAMPLE_RATE_HZ 48000
+
+/* Supported HDMI Audio channels */
+enum hdmi_audio_channels {
+ AUDIO_CHANNEL_2 = 2,
+ AUDIO_CHANNEL_3,
+ AUDIO_CHANNEL_4,
+ AUDIO_CHANNEL_5,
+ AUDIO_CHANNEL_6,
+ AUDIO_CHANNEL_7,
+ AUDIO_CHANNEL_8,
+};
+
+/* parameters for clock regeneration */
+struct hdmi_audio_acr {
+ u32 n;
+ u32 cts;
+};
+
+enum hdmi_audio_sample_rates {
+ AUDIO_SAMPLE_RATE_32KHZ,
+ AUDIO_SAMPLE_RATE_44_1KHZ,
+ AUDIO_SAMPLE_RATE_48KHZ,
+ AUDIO_SAMPLE_RATE_88_2KHZ,
+ AUDIO_SAMPLE_RATE_96KHZ,
+ AUDIO_SAMPLE_RATE_176_4KHZ,
+ AUDIO_SAMPLE_RATE_192KHZ,
+ AUDIO_SAMPLE_RATE_MAX
+};
+
+struct hdmi_audio {
+ struct dss_io_data *io;
+ struct msm_hdmi_audio_setup_params params;
+ struct switch_dev sdev;
+ u32 pclk;
+ bool ack_enabled;
+ bool audio_ack_enabled;
+ atomic_t ack_pending;
+};
+
+static void hdmi_audio_get_audio_sample_rate(u32 *sample_rate_hz)
+{
+ u32 rate = *sample_rate_hz;
+
+ switch (rate) {
+ case 32000:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_32KHZ;
+ break;
+ case 44100:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_44_1KHZ;
+ break;
+ case 48000:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_48KHZ;
+ break;
+ case 88200:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_88_2KHZ;
+ break;
+ case 96000:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_96KHZ;
+ break;
+ case 176400:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_176_4KHZ;
+ break;
+ case 192000:
+ *sample_rate_hz = AUDIO_SAMPLE_RATE_192KHZ;
+ break;
+ default:
+ pr_debug("%d unchanged\n", rate);
+ break;
+ }
+}
+
+static void hdmi_audio_get_acr_param(u32 pclk, u32 fs,
+ struct hdmi_audio_acr *acr)
+{
+ u32 div, mul;
+
+ if (!acr) {
+ pr_err("invalid data\n");
+ return;
+ }
+
+ /*
+ * as per HDMI specification, N/CTS = (128*fs)/pclk.
+ * get the ratio using this formula.
+ */
+ acr->n = HDMI_ACR_N_MULTIPLIER * fs;
+ acr->cts = pclk;
+
+ /* get the greatest common divisor for the ratio */
+ div = gcd(acr->n, acr->cts);
+
+ /* get the n and cts values wrt N/CTS formula */
+ acr->n /= div;
+ acr->cts /= div;
+
+ /*
+ * as per HDMI specification, 300 <= 128*fs/N <= 1500
+ * with a target of 128*fs/N = 1000. To get closest
+ * value without truncating fractional values, find
+ * the corresponding multiplier
+ */
+ mul = ((HDMI_ACR_N_MULTIPLIER * fs / HDMI_KHZ_TO_HZ)
+ + (acr->n - 1)) / acr->n;
+
+ acr->n *= mul;
+ acr->cts *= mul;
+}
+
+static void hdmi_audio_acr_enable(struct hdmi_audio *audio)
+{
+ struct dss_io_data *io;
+ struct hdmi_audio_acr acr;
+ struct msm_hdmi_audio_setup_params *params;
+ u32 pclk, layout, multiplier, sample_rate;
+ u32 acr_pkt_ctl, aud_pkt_ctl2, acr_reg_cts, acr_reg_n;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = audio->io;
+ params = &audio->params;
+ pclk = audio->pclk;
+ sample_rate = params->sample_rate_hz;
+
+ hdmi_audio_get_acr_param(pclk * HDMI_KHZ_TO_HZ, sample_rate, &acr);
+ hdmi_audio_get_audio_sample_rate(&sample_rate);
+
+ layout = AUDIO_CHANNEL_2 == params->num_of_channels ? 0 : 1;
+
+ pr_debug("n=%u, cts=%u, layout=%u\n", acr.n, acr.cts, layout);
+
+ /* AUDIO_PRIORITY | SOURCE */
+ acr_pkt_ctl = BIT(31) | BIT(8);
+
+ /* N_MULTIPLE(multiplier) */
+ acr_pkt_ctl |= (multiplier & 0x7) << 16;
+
+ switch (sample_rate) {
+ case AUDIO_SAMPLE_RATE_44_1KHZ:
+ acr_pkt_ctl |= 0x2 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_44_0;
+ acr_reg_n = HDMI_ACR_44_1;
+ break;
+ case AUDIO_SAMPLE_RATE_48KHZ:
+ acr_pkt_ctl |= 0x3 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_48_0;
+ acr_reg_n = HDMI_ACR_48_1;
+ break;
+ case AUDIO_SAMPLE_RATE_192KHZ:
+ multiplier = 4;
+ acr.n >>= 2;
+
+ acr_pkt_ctl |= 0x3 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_48_0;
+ acr_reg_n = HDMI_ACR_48_1;
+ break;
+ case AUDIO_SAMPLE_RATE_176_4KHZ:
+ multiplier = 4;
+ acr.n >>= 2;
+
+ acr_pkt_ctl |= 0x2 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_44_0;
+ acr_reg_n = HDMI_ACR_44_1;
+ break;
+ case AUDIO_SAMPLE_RATE_96KHZ:
+ multiplier = 2;
+ acr.n >>= 1;
+
+ acr_pkt_ctl |= 0x3 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_48_0;
+ acr_reg_n = HDMI_ACR_48_1;
+ break;
+ case AUDIO_SAMPLE_RATE_88_2KHZ:
+ multiplier = 2;
+ acr.n >>= 1;
+
+ acr_pkt_ctl |= 0x2 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_44_0;
+ acr_reg_n = HDMI_ACR_44_1;
+ break;
+ default:
+ multiplier = 1;
+
+ acr_pkt_ctl |= 0x1 << 4;
+ acr.cts <<= 12;
+
+ acr_reg_cts = HDMI_ACR_32_0;
+ acr_reg_n = HDMI_ACR_32_1;
+ break;
+ }
+
+ aud_pkt_ctl2 = BIT(0) | (layout << 1);
+
+ /* SEND | CONT */
+ acr_pkt_ctl |= BIT(0) | BIT(1);
+
+ DSS_REG_W(io, acr_reg_cts, acr.cts);
+ DSS_REG_W(io, acr_reg_n, acr.n);
+ DSS_REG_W(io, HDMI_ACR_PKT_CTRL, acr_pkt_ctl);
+ DSS_REG_W(io, HDMI_AUDIO_PKT_CTRL2, aud_pkt_ctl2);
+}
+
+static void hdmi_audio_acr_setup(struct hdmi_audio *audio, bool on)
+{
+ if (on)
+ hdmi_audio_acr_enable(audio);
+ else
+ DSS_REG_W(audio->io, HDMI_ACR_PKT_CTRL, 0);
+}
+
+static void hdmi_audio_infoframe_setup(struct hdmi_audio *audio, bool enabled)
+{
+ struct dss_io_data *io = NULL;
+ u32 channels, channel_allocation, level_shift, down_mix, layout;
+ u32 hdmi_debug_reg = 0, audio_info_0_reg = 0, audio_info_1_reg = 0;
+ u32 audio_info_ctrl_reg, aud_pck_ctrl_2_reg;
+ u32 check_sum, sample_present;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ io = audio->io;
+ if (!io->base) {
+ pr_err("core io not inititalized\n");
+ return;
+ }
+
+ audio_info_ctrl_reg = DSS_REG_R(io, HDMI_INFOFRAME_CTRL0);
+ audio_info_ctrl_reg &= ~0xF0;
+
+ if (!enabled)
+ goto end;
+
+ channels = audio->params.num_of_channels - 1;
+ channel_allocation = audio->params.channel_allocation;
+ level_shift = audio->params.level_shift;
+ down_mix = audio->params.down_mix;
+ sample_present = audio->params.sample_present;
+
+ layout = AUDIO_CHANNEL_2 == audio->params.num_of_channels ? 0 : 1;
+ aud_pck_ctrl_2_reg = BIT(0) | (layout << 1);
+ DSS_REG_W(io, HDMI_AUDIO_PKT_CTRL2, aud_pck_ctrl_2_reg);
+
+ audio_info_1_reg |= channel_allocation & 0xFF;
+ audio_info_1_reg |= ((level_shift & 0xF) << 11);
+ audio_info_1_reg |= ((down_mix & 0x1) << 15);
+
+ check_sum = 0;
+ check_sum += HDMI_AUDIO_INFO_FRAME_PACKET_HEADER;
+ check_sum += HDMI_AUDIO_INFO_FRAME_PACKET_VERSION;
+ check_sum += HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH;
+ check_sum += channels;
+ check_sum += channel_allocation;
+ check_sum += (level_shift & 0xF) << 3 | (down_mix & 0x1) << 7;
+ check_sum &= 0xFF;
+ check_sum = (u8) (256 - check_sum);
+
+ audio_info_0_reg |= check_sum & 0xFF;
+ audio_info_0_reg |= ((channels & 0x7) << 8);
+
+ /* Enable Audio InfoFrame Transmission */
+ audio_info_ctrl_reg |= 0xF0;
+
+ if (layout) {
+ /* Set the Layout bit */
+ hdmi_debug_reg |= BIT(4);
+
+ /* Set the Sample Present bits */
+ hdmi_debug_reg |= sample_present & 0xF;
+ }
+end:
+ DSS_REG_W(io, HDMI_DEBUG, hdmi_debug_reg);
+ DSS_REG_W(io, HDMI_AUDIO_INFO0, audio_info_0_reg);
+ DSS_REG_W(io, HDMI_AUDIO_INFO1, audio_info_1_reg);
+ DSS_REG_W(io, HDMI_INFOFRAME_CTRL0, audio_info_ctrl_reg);
+}
+
+static int hdmi_audio_on(void *ctx, u32 pclk,
+ struct msm_hdmi_audio_setup_params *params)
+{
+ struct hdmi_audio *audio = ctx;
+ int rc = 0;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ rc = -EINVAL;
+ goto end;
+ }
+
+ audio->pclk = pclk;
+ audio->params = *params;
+
+ if (!audio->params.num_of_channels) {
+ audio->params.sample_rate_hz = DEFAULT_AUDIO_SAMPLE_RATE_HZ;
+ audio->params.num_of_channels = AUDIO_CHANNEL_2;
+ }
+
+ hdmi_audio_acr_setup(audio, true);
+ hdmi_audio_infoframe_setup(audio, true);
+
+ pr_debug("HDMI Audio: Enabled\n");
+end:
+ return rc;
+}
+
+static void hdmi_audio_off(void *ctx)
+{
+ struct hdmi_audio *audio = ctx;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ hdmi_audio_infoframe_setup(audio, false);
+ hdmi_audio_acr_setup(audio, false);
+
+ pr_debug("HDMI Audio: Disabled\n");
+}
+
+static void hdmi_audio_notify(void *ctx, int val)
+{
+ struct hdmi_audio *audio = ctx;
+ int state = 0;
+ bool switched;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ state = audio->sdev.state;
+ if (state == val)
+ return;
+
+ if (audio->ack_enabled &&
+ atomic_read(&audio->ack_pending)) {
+ pr_err("%s ack pending, not notifying %s\n",
+ state ? "connect" : "disconnect",
+ val ? "connect" : "disconnect");
+ return;
+ }
+
+ switch_set_state(&audio->sdev, val);
+ switched = audio->sdev.state != state;
+
+ if (audio->ack_enabled && switched)
+ atomic_set(&audio->ack_pending, 1);
+
+ pr_debug("audio %s %s\n", switched ? "switched to" : "same as",
+ audio->sdev.state ? "HDMI" : "SPKR");
+}
+
+static void hdmi_audio_ack(void *ctx, u32 ack, u32 hpd)
+{
+ struct hdmi_audio *audio = ctx;
+ u32 ack_hpd;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ if (ack & AUDIO_ACK_SET_ENABLE) {
+ audio->ack_enabled = ack & AUDIO_ACK_ENABLE ?
+ true : false;
+
+ pr_debug("audio ack feature %s\n",
+ audio->ack_enabled ? "enabled" : "disabled");
+ return;
+ }
+
+ if (!audio->ack_enabled)
+ return;
+
+ atomic_set(&audio->ack_pending, 0);
+
+ ack_hpd = ack & AUDIO_ACK_CONNECT;
+
+ pr_debug("acknowledging %s\n",
+ ack_hpd ? "connect" : "disconnect");
+
+ if (ack_hpd != hpd) {
+ pr_debug("unbalanced audio state, ack %d, hpd %d\n",
+ ack_hpd, hpd);
+
+ hdmi_audio_notify(ctx, hpd);
+ }
+}
+
+static void hdmi_audio_reset(void *ctx)
+{
+ struct hdmi_audio *audio = ctx;
+
+ if (!audio) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ atomic_set(&audio->ack_pending, 0);
+}
+
+static void hdmi_audio_status(void *ctx, struct hdmi_audio_status *status)
+{
+ struct hdmi_audio *audio = ctx;
+
+ if (!audio || !status) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ status->ack_enabled = audio->ack_enabled;
+ status->ack_pending = atomic_read(&audio->ack_pending);
+ status->switched = audio->sdev.state;
+}
+
+/**
+ * hdmi_audio_register() - audio registeration function
+ * @data: registeration initialization data
+ *
+ * This API configures audio module for client to use HDMI audio.
+ * Provides audio functionalities which client can call.
+ * Initializes internal data structures.
+ *
+ * Return: pointer to audio data that client needs to pass on
+ * calling audio functions.
+ */
+void *hdmi_audio_register(struct hdmi_audio_init_data *data)
+{
+ struct hdmi_audio *audio = NULL;
+ int rc = 0;
+
+ if (!data)
+ goto end;
+
+ audio = kzalloc(sizeof(*audio), GFP_KERNEL);
+ if (!audio)
+ goto end;
+
+ audio->sdev.name = "hdmi_audio";
+ rc = switch_dev_register(&audio->sdev);
+ if (rc) {
+ pr_err("audio switch registration failed\n");
+ kzfree(audio);
+ goto end;
+ }
+
+ audio->io = data->io;
+
+ data->ops->on = hdmi_audio_on;
+ data->ops->off = hdmi_audio_off;
+ data->ops->notify = hdmi_audio_notify;
+ data->ops->ack = hdmi_audio_ack;
+ data->ops->reset = hdmi_audio_reset;
+ data->ops->status = hdmi_audio_status;
+end:
+ return audio;
+}
+
+/**
+ * hdmi_audio_unregister() - unregister audio module
+ * @ctx: audio module's data
+ *
+ * Delete audio module's instance and allocated resources
+ */
+void hdmi_audio_unregister(void *ctx)
+{
+ struct hdmi_audio *audio = ctx;
+
+ if (audio) {
+ switch_dev_unregister(&audio->sdev);
+ kfree(ctx);
+ }
+}
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_audio.h b/drivers/video/fbdev/msm/mdss_hdmi_audio.h
new file mode 100644
index 000000000000..c53bfd9b1ff2
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_hdmi_audio.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MDSS_HDMI_AUDIO_H__
+#define __MDSS_HDMI_AUDIO_H__
+
+#include <linux/mdss_io_util.h>
+#include <linux/msm_hdmi.h>
+
+#define AUDIO_ACK_SET_ENABLE BIT(5)
+#define AUDIO_ACK_ENABLE BIT(4)
+#define AUDIO_ACK_CONNECT BIT(0)
+
+/**
+ * struct hdmi_audio_status - hdmi audio current status info
+ * @ack_pending: notification acknowledgment status
+ * @ack_enabled: acknowledgment feature is enabled or disabled
+ * @switched: audio notification status for routing
+ *
+ * Data for client to query about the current status of audio
+ */
+struct hdmi_audio_status {
+ bool ack_pending;
+ bool ack_enabled;
+ bool switched;
+};
+
+/**
+ * struct hdmi_audio_ops - audio operations for clients to call
+ * @on: function pointer to enable audio
+ * @reset: function pointer to reset the audio current status to default
+ * @status: function pointer to get the current status of audio
+ * @notify: function pointer to notify other modules for audio routing
+ * @ack: function pointer to acknowledge audio routing change
+ *
+ * Provides client operations for audio functionalities
+ */
+struct hdmi_audio_ops {
+ int (*on)(void *ctx, u32 pclk,
+ struct msm_hdmi_audio_setup_params *params);
+ void (*off)(void *ctx);
+ void (*reset)(void *ctx);
+ void (*status)(void *ctx, struct hdmi_audio_status *status);
+ void (*notify)(void *ctx, int val);
+ void (*ack)(void *ctx, u32 ack, u32 hpd);
+};
+
+/**
+ * struct hdmi_audio_init_data - data needed for initializing audio module
+ * @io: pointer to register access related data
+ * @ops: pointer to populate operation functions.
+ *
+ * Defines the data needed to be provided while initializing audio module
+ */
+struct hdmi_audio_init_data {
+ struct dss_io_data *io;
+ struct hdmi_audio_ops *ops;
+};
+
+void *hdmi_audio_register(struct hdmi_audio_init_data *data);
+void hdmi_audio_unregister(void *data);
+
+#endif /* __MDSS_HDMI_AUDIO_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 26c63ec417a6..f5c45571f0d2 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2016, 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
@@ -114,6 +114,13 @@ struct hdmi_edid_sink_caps {
bool ind_view_support;
};
+struct hdmi_edid_override_data {
+ int scramble;
+ int sink_mode;
+ int format;
+ int vic;
+};
+
struct hdmi_edid_ctrl {
u8 pt_scan_info;
u8 it_scan_info;
@@ -134,11 +141,12 @@ struct hdmi_edid_ctrl {
u8 edid_buf[MAX_EDID_SIZE];
char vendor_id[EDID_VENDOR_ID_SIZE];
bool keep_resv_timings;
- u32 edid_override;
+ bool edid_override;
struct hdmi_edid_sink_data sink_data;
struct hdmi_edid_init_data init_data;
struct hdmi_edid_sink_caps sink_caps;
+ struct hdmi_edid_override_data override_data;
};
static bool hdmi_edid_is_mode_supported(struct hdmi_edid_ctrl *edid_ctrl,
@@ -315,12 +323,8 @@ static DEVICE_ATTR(spkr_alloc_data_block, S_IRUGO,
static ssize_t hdmi_edid_sysfs_wta_modes(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- int scrambling, vic, format, sink;
ssize_t ret = strnlen(buf, PAGE_SIZE);
- int rc;
struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
- struct hdmi_edid_sink_data *sd;
- struct msm_hdmi_mode_timing_info info = {0};
if (!edid_ctrl) {
DEV_ERR("%s: invalid ctrl\n", __func__);
@@ -328,52 +332,16 @@ static ssize_t hdmi_edid_sysfs_wta_modes(struct device *dev,
goto error;
}
- sd = &edid_ctrl->sink_data;
-
if (sscanf(buf, "%d %d %d %d",
- &scrambling, &sink, &format, &vic) != 4) {
+ &edid_ctrl->override_data.scramble,
+ &edid_ctrl->override_data.sink_mode,
+ &edid_ctrl->override_data.format,
+ &edid_ctrl->override_data.vic) != 4) {
DEV_ERR("could not read input\n");
ret = -EINVAL;
goto bail;
}
- if ((sink != SINK_MODE_DVI && sink != SINK_MODE_HDMI) ||
- !(format & (MSM_HDMI_RGB_888_24BPP_FORMAT |
- MSM_HDMI_YUV_420_12BPP_FORMAT)) ||
- vic <= HDMI_VFRMT_UNKNOWN || vic >= HDMI_VFRMT_MAX) {
- DEV_ERR("%s: invalid input: sink %d, format %d, vic %d\n",
- __func__, sink, format, vic);
- ret = -EINVAL;
- goto bail;
- }
-
- rc = hdmi_get_supported_mode(&info,
- &edid_ctrl->init_data.ds_data, vic);
- if (rc) {
- DEV_ERR("%s: error getting res details\n", __func__);
- ret = -EINVAL;
- goto bail;
- }
-
- if (!hdmi_edid_is_mode_supported(edid_ctrl, &info)) {
- DEV_ERR("%s: %d vic not supported\n", __func__, vic);
- ret = -EINVAL;
- goto bail;
- }
-
- sd->num_of_elements = 1;
- sd->disp_mode_list[0].video_format = vic;
-
- if (format & MSM_HDMI_RGB_888_24BPP_FORMAT)
- sd->disp_mode_list[0].rgb_support = true;
-
- if (format & MSM_HDMI_YUV_420_12BPP_FORMAT)
- sd->disp_mode_list[0].y420_support = true;
-
- edid_ctrl->sink_mode = sink;
- edid_ctrl->sink_caps.scramble_support = !!scrambling;
- edid_ctrl->sink_caps.scdc_present = !!scrambling;
-
edid_ctrl->edid_override = true;
return ret;
bail:
@@ -389,17 +357,26 @@ static ssize_t hdmi_edid_sysfs_rda_modes(struct device *dev,
ssize_t ret = 0;
int i;
struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
+ u32 num_of_elements = 0;
+ struct disp_mode_info *video_mode;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -EINVAL;
}
+ num_of_elements = edid_ctrl->sink_data.num_of_elements;
+ video_mode = edid_ctrl->sink_data.disp_mode_list;
+
+ if (edid_ctrl->edid_override && (edid_ctrl->override_data.vic > 0)) {
+ num_of_elements = 1;
+ edid_ctrl->sink_data.disp_mode_list[0].video_format =
+ edid_ctrl->override_data.vic;
+ }
+
buf[0] = 0;
- if (edid_ctrl->sink_data.num_of_elements) {
- struct disp_mode_info *video_mode =
- edid_ctrl->sink_data.disp_mode_list;
- for (i = 0; i < edid_ctrl->sink_data.num_of_elements; i++) {
+ if (num_of_elements) {
+ for (i = 0; i < num_of_elements; i++) {
if (ret > 0)
ret += scnprintf(buf + ret, PAGE_SIZE - ret,
",%d", video_mode[i].video_format);
@@ -420,6 +397,65 @@ static ssize_t hdmi_edid_sysfs_rda_modes(struct device *dev,
static DEVICE_ATTR(edid_modes, S_IRUGO | S_IWUSR, hdmi_edid_sysfs_rda_modes,
hdmi_edid_sysfs_wta_modes);
+static ssize_t hdmi_edid_sysfs_rda_res_info_data(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ u32 i, no_of_elem, offset = 0;
+ struct msm_hdmi_mode_timing_info info = {0};
+ struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev);
+ struct disp_mode_info *minfo = NULL;
+
+ if (!edid_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ no_of_elem = edid_ctrl->sink_data.num_of_elements;
+ minfo = edid_ctrl->sink_data.disp_mode_list;
+
+ if (edid_ctrl->edid_override && (edid_ctrl->override_data.vic > 0)) {
+ no_of_elem = 1;
+ minfo[0].video_format = edid_ctrl->override_data.vic;
+ }
+
+ for (i = 0; i < no_of_elem; i++) {
+ ret = hdmi_get_supported_mode(&info,
+ &edid_ctrl->init_data.ds_data,
+ minfo->video_format);
+
+ if (edid_ctrl->edid_override &&
+ (edid_ctrl->override_data.format > 0))
+ info.pixel_formats = edid_ctrl->override_data.format;
+ else
+ info.pixel_formats =
+ (minfo->rgb_support ?
+ MSM_HDMI_RGB_888_24BPP_FORMAT : 0) |
+ (minfo->y420_support ?
+ MSM_HDMI_YUV_420_12BPP_FORMAT : 0);
+
+ minfo++;
+ if (ret || !info.supported)
+ continue;
+
+ offset += scnprintf(buf + offset, PAGE_SIZE - offset,
+ "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ info.video_format, info.active_h,
+ info.front_porch_h, info.pulse_width_h,
+ info.back_porch_h, info.active_low_h,
+ info.active_v, info.front_porch_v,
+ info.pulse_width_v, info.back_porch_v,
+ info.active_low_v, info.pixel_freq,
+ info.refresh_rate, info.interlaced,
+ info.supported, info.ar,
+ info.pixel_formats);
+ }
+
+ return offset;
+}
+static DEVICE_ATTR(res_info_data, S_IRUGO, hdmi_edid_sysfs_rda_res_info_data,
+ NULL);
+
static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -477,14 +513,25 @@ static ssize_t hdmi_edid_sysfs_rda_res_info(struct device *dev,
}
}
+ if (edid_ctrl->edid_override && (edid_ctrl->override_data.vic > 0)) {
+ no_of_elem = 1;
+ minfo[0].video_format = edid_ctrl->override_data.vic;
+ }
+
for (; i < no_of_elem && size_to_write < PAGE_SIZE; i++) {
ret = hdmi_get_supported_mode(&info,
&edid_ctrl->init_data.ds_data,
minfo->video_format);
- info.pixel_formats =
- (minfo->rgb_support ? MSM_HDMI_RGB_888_24BPP_FORMAT : 0) |
- (minfo->y420_support ? MSM_HDMI_YUV_420_12BPP_FORMAT : 0);
+ if (edid_ctrl->edid_override &&
+ (edid_ctrl->override_data.format > 0))
+ info.pixel_formats = edid_ctrl->override_data.format;
+ else
+ info.pixel_formats =
+ (minfo->rgb_support ?
+ MSM_HDMI_RGB_888_24BPP_FORMAT : 0) |
+ (minfo->y420_support ?
+ MSM_HDMI_YUV_420_12BPP_FORMAT : 0);
minfo++;
if (ret || !info.supported)
@@ -725,6 +772,7 @@ static struct attribute *hdmi_edid_fs_attrs[] = {
&dev_attr_edid_audio_latency.attr,
&dev_attr_edid_video_latency.attr,
&dev_attr_res_info.attr,
+ &dev_attr_res_info_data.attr,
&dev_attr_add_res.attr,
NULL,
};
@@ -2102,11 +2150,6 @@ int hdmi_edid_parser(void *input)
goto err_invalid_data;
}
- if (edid_ctrl->edid_override) {
- DEV_DBG("edid override enabled\n");
- goto err_invalid_data;
- }
-
/* reset edid data for new hdmi connection */
hdmi_edid_reset_parser(edid_ctrl);
@@ -2244,13 +2287,20 @@ end:
u32 hdmi_edid_get_sink_mode(void *input)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+ bool sink_mode;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return 0;
}
- return edid_ctrl->sink_mode;
+ if (edid_ctrl->edid_override &&
+ (edid_ctrl->override_data.sink_mode != -1))
+ sink_mode = edid_ctrl->override_data.sink_mode;
+ else
+ sink_mode = edid_ctrl->sink_mode;
+
+ return sink_mode;
} /* hdmi_edid_get_sink_mode */
bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode)
@@ -2280,24 +2330,39 @@ bool hdmi_edid_is_s3d_mode_supported(void *input, u32 video_mode, u32 s3d_mode)
bool hdmi_edid_get_scdc_support(void *input)
{
struct hdmi_edid_ctrl *edid_ctrl = input;
+ bool scdc_present;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return false;
}
- return edid_ctrl->sink_caps.scdc_present;
+ if (edid_ctrl->edid_override &&
+ (edid_ctrl->override_data.scramble != -1))
+ scdc_present = edid_ctrl->override_data.scramble;
+ else
+ scdc_present = edid_ctrl->sink_caps.scdc_present;
+
+ return scdc_present;
}
bool hdmi_edid_get_sink_scrambler_support(void *input)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+ bool scramble_support;
if (!edid_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return 0;
}
- return edid_ctrl->sink_caps.scramble_support;
+
+ if (edid_ctrl->edid_override &&
+ (edid_ctrl->override_data.scramble != -1))
+ scramble_support = edid_ctrl->override_data.scramble;
+ else
+ scramble_support = edid_ctrl->sink_caps.scramble_support;
+
+ return scramble_support;
}
int hdmi_edid_get_audio_blk(void *input, struct msm_hdmi_audio_edid_blk *blk)
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
index 85175d69d6f1..fb59d0b03afe 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
@@ -47,6 +47,11 @@ enum hdmi_hdcp2p2_sink_status {
SINK_CONNECTED
};
+enum hdmi_auth_status {
+ HDMI_HDCP_AUTH_STATUS_FAILURE,
+ HDMI_HDCP_AUTH_STATUS_SUCCESS
+};
+
struct hdmi_hdcp2p2_ctrl {
atomic_t auth_state;
bool tethered;
@@ -60,6 +65,7 @@ struct hdmi_hdcp2p2_ctrl {
struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */
enum hdmi_hdcp_wakeup_cmd wakeup_cmd;
+ enum hdmi_auth_status auth_status;
char *send_msg_buf;
uint32_t send_msg_len;
uint32_t timeout;
@@ -156,6 +162,11 @@ static int hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
if (hdmi_hdcp2p2_copy_buf(ctrl, data))
goto exit;
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_SUCCESS;
+ else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_FAILURE;
+
if (ctrl->tethered)
goto exit;
@@ -820,9 +831,7 @@ static void hdmi_hdcp2p2_auth_status(struct hdmi_hdcp2p2_ctrl *ctrl)
return;
}
- if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED) {
- hdmi_hdcp2p2_auth_failed(ctrl);
- } else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS) {
+ if (ctrl->auth_status == HDMI_HDCP_AUTH_STATUS_SUCCESS) {
ctrl->init_data.notify_status(ctrl->init_data.cb_data,
HDCP_STATE_AUTHENTICATED);
@@ -830,6 +839,8 @@ static void hdmi_hdcp2p2_auth_status(struct hdmi_hdcp2p2_ctrl *ctrl)
if (ctrl->tethered)
hdmi_hdcp2p2_link_check(ctrl);
+ } else {
+ hdmi_hdcp2p2_auth_failed(ctrl);
}
}
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index fc7ed49f8536..a902fd7b82c7 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -20,7 +20,6 @@
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/types.h>
-#include <linux/msm_hdmi.h>
#include <linux/hdcp_qseecom.h>
#include <linux/clk.h>
@@ -32,6 +31,7 @@
#include "mdss_hdmi_edid.h"
#include "mdss_hdmi_hdcp.h"
#include "mdss_hdmi_tx.h"
+#include "mdss_hdmi_audio.h"
#include "mdss.h"
#include "mdss_panel.h"
#include "mdss_hdmi_mhl.h"
@@ -49,10 +49,6 @@
#define HPD_DISCONNECT_POLARITY 0
#define HPD_CONNECT_POLARITY 1
-#define AUDIO_ACK_SET_ENABLE BIT(5)
-#define AUDIO_ACK_ENABLE BIT(4)
-#define AUDIO_ACK_CONNECT BIT(0)
-
/*
* Audio engine may take 1 to 3 sec to shutdown
* in normal cases. To handle worst cases, making
@@ -76,9 +72,6 @@
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
-#define HDMI_TX_KHZ_TO_HZ 1000
-#define HDMI_TX_MHZ_TO_HZ 1000000
-
/* Maximum pixel clock rates for hdmi tx */
#define HDMI_DEFAULT_MAX_PCLK_RATE 148500
#define HDMI_TX_3_MAX_PCLK_RATE 297000
@@ -87,15 +80,6 @@
/* Enable HDCP by default */
static bool hdcp_feature_on = true;
-/* Supported HDMI Audio channels */
-#define MSM_HDMI_AUDIO_CHANNEL_2 2
-#define MSM_HDMI_AUDIO_CHANNEL_3 3
-#define MSM_HDMI_AUDIO_CHANNEL_4 4
-#define MSM_HDMI_AUDIO_CHANNEL_5 5
-#define MSM_HDMI_AUDIO_CHANNEL_6 6
-#define MSM_HDMI_AUDIO_CHANNEL_7 7
-#define MSM_HDMI_AUDIO_CHANNEL_8 8
-
/* AVI INFOFRAME DATA */
#define NUM_MODES_AVI 20
#define AVI_MAX_DATA_BYTES 13
@@ -155,17 +139,6 @@ enum {
(byte = (byte & ~(BIT(4) | BIT(5))) |\
((bits & (BIT(0) | BIT(1))) << 4))
-enum msm_hdmi_supported_audio_sample_rates {
- AUDIO_SAMPLE_RATE_32KHZ,
- AUDIO_SAMPLE_RATE_44_1KHZ,
- AUDIO_SAMPLE_RATE_48KHZ,
- AUDIO_SAMPLE_RATE_88_2KHZ,
- AUDIO_SAMPLE_RATE_96KHZ,
- AUDIO_SAMPLE_RATE_176_4KHZ,
- AUDIO_SAMPLE_RATE_192KHZ,
- AUDIO_SAMPLE_RATE_MAX
-};
-
enum hdmi_tx_hpd_states {
HPD_OFF,
HPD_ON,
@@ -179,24 +152,12 @@ enum hdmi_tx_res_states {
RESOLUTION_CHANGED
};
-/* parameters for clock regeneration */
-struct hdmi_tx_audio_acr {
- u32 n;
- u32 cts;
-};
-
-struct hdmi_tx_audio_acr_arry {
- u32 pclk;
- struct hdmi_tx_audio_acr lut[AUDIO_SAMPLE_RATE_MAX];
-};
-
static int hdmi_tx_set_mhl_hpd(struct platform_device *pdev, uint8_t on);
static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on);
static irqreturn_t hdmi_tx_isr(int irq, void *data);
static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl);
static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl,
enum hdmi_tx_power_module_type module, int enable);
-static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl);
static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl);
static void hdmi_tx_set_vendor_specific_infoframe(
struct hdmi_tx_ctrl *hdmi_ctrl);
@@ -238,35 +199,6 @@ const char *hdmi_pm_name(enum hdmi_tx_power_module_type module)
}
} /* hdmi_pm_name */
-/* Audio constants lookup table for hdmi_tx_audio_acr_setup */
-/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
-static const struct hdmi_tx_audio_acr_arry hdmi_tx_audio_acr_lut[] = {
- /* 25.200MHz */
- {25200, {{4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000},
- {12288, 25200}, {25088, 28000}, {24576, 25200} } },
- /* 27.000MHz */
- {27000, {{4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000},
- {12288, 27000}, {25088, 30000}, {24576, 27000} } },
- /* 27.027MHz */
- {27027, {{4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030},
- {12288, 27027}, {25088, 30030}, {24576, 27027} } },
- /* 74.250MHz */
- {74250, {{4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500},
- {12288, 74250}, {25088, 82500}, {24576, 74250} } },
- /* 148.500MHz */
- {148500, {{4096, 148500}, {6272, 165000}, {6144, 148500},
- {12544, 165000}, {12288, 148500}, {25088, 165000},
- {24576, 148500} } },
- /* 297.000MHz */
- {297000, {{3072, 222750}, {4704, 247500}, {5120, 247500},
- {9408, 247500}, {10240, 247500}, {18816, 247500},
- {20480, 247500} } },
- /* 594.000MHz */
- {594000, {{3072, 445500}, {9408, 990000}, {6144, 594000},
- {18816, 990000}, {12288, 594000}, {37632, 990000},
- {24576, 594000} } },
-};
-
static int hdmi_tx_get_version(struct hdmi_tx_ctrl *hdmi_ctrl)
{
int rc;
@@ -425,6 +357,16 @@ static const char *hdmi_tx_io_name(u32 type)
}
} /* hdmi_tx_io_name */
+static void hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ if (hdmi_ctrl && hdmi_ctrl->audio_ops.on) {
+ u32 pclk = hdmi_tx_setup_tmds_clk_rate(hdmi_ctrl);
+
+ hdmi_ctrl->audio_ops.on(hdmi_ctrl->audio_data,
+ pclk, &hdmi_ctrl->audio_params);
+ }
+}
+
static int hdmi_tx_get_vic_from_panel_info(struct hdmi_tx_ctrl *hdmi_ctrl,
struct mdss_panel_info *pinfo)
{
@@ -546,41 +488,9 @@ static inline void hdmi_tx_send_cable_notification(
static inline void hdmi_tx_set_audio_switch_node(
struct hdmi_tx_ctrl *hdmi_ctrl, int val)
{
- int state = 0;
-
- if (!hdmi_ctrl) {
- DEV_ERR("%s: invalid input\n", __func__);
- return;
- }
-
- state = hdmi_ctrl->audio_sdev.state;
-
- if (state == val)
- return;
-
- if (hdmi_ctrl->audio_ack_enabled &&
- atomic_read(&hdmi_ctrl->audio_ack_pending)) {
- DEV_ERR("%s: %s ack pending, not notifying %s\n", __func__,
- state ? "connect" : "disconnect",
- val ? "connect" : "disconnect");
- return;
- }
-
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) &&
- hdmi_tx_is_cea_format(hdmi_ctrl->vid_cfg.vic)) {
- bool switched;
-
- switch_set_state(&hdmi_ctrl->audio_sdev, val);
- switched = hdmi_ctrl->audio_sdev.state != state;
-
- if (hdmi_ctrl->audio_ack_enabled && switched)
- atomic_set(&hdmi_ctrl->audio_ack_pending, 1);
-
- DEV_INFO("%s: audio state %s %d\n", __func__,
- switched ? "switched to" : "is same",
- hdmi_ctrl->audio_sdev.state);
- }
-} /* hdmi_tx_set_audio_switch_node */
+ if (hdmi_ctrl && hdmi_ctrl->audio_ops.notify)
+ hdmi_ctrl->audio_ops.notify(hdmi_ctrl->audio_data, val);
+}
static void hdmi_tx_wait_for_audio_engine(struct hdmi_tx_ctrl *hdmi_ctrl)
{
@@ -737,6 +647,14 @@ static ssize_t hdmi_tx_sysfs_wta_edid(struct device *dev,
}
mutex_lock(&hdmi_ctrl->tx_lock);
+ if (edid_size < EDID_BLOCK_SIZE) {
+ DEV_DBG("%s: disabling custom edid\n", __func__);
+
+ ret = -EINVAL;
+ hdmi_ctrl->custom_edid = false;
+ goto end;
+ }
+
memset(hdmi_ctrl->edid_buf, 0, hdmi_ctrl->edid_buf_size);
while (edid_size--) {
@@ -799,7 +717,6 @@ static ssize_t hdmi_tx_sysfs_wta_audio_cb(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ack, rc = 0;
- int ack_hpd;
ssize_t ret = strnlen(buf, PAGE_SIZE);
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
@@ -817,34 +734,113 @@ static ssize_t hdmi_tx_sysfs_wta_audio_cb(struct device *dev,
goto end;
}
- if (ack & AUDIO_ACK_SET_ENABLE) {
- hdmi_ctrl->audio_ack_enabled = ack & AUDIO_ACK_ENABLE ?
- true : false;
+ if (hdmi_ctrl->audio_ops.ack)
+ hdmi_ctrl->audio_ops.ack(hdmi_ctrl->audio_data,
+ ack, hdmi_ctrl->hpd_state);
+end:
+ return ret;
+}
- DEV_INFO("%s: audio ack feature %s\n", __func__,
- hdmi_ctrl->audio_ack_enabled ? "enabled" : "disabled");
+static ssize_t hdmi_tx_sysfs_wta_hot_plug(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int hot_plug, rc;
+ struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
+
+ hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hdmi_ctrl->tx_lock);
+ rc = kstrtoint(buf, 10, &hot_plug);
+ if (rc) {
+ DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc);
goto end;
}
- if (!hdmi_ctrl->audio_ack_enabled)
+ hdmi_ctrl->hpd_state = !!hot_plug;
+
+ queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work);
+
+ rc = strnlen(buf, PAGE_SIZE);
+end:
+ mutex_unlock(&hdmi_ctrl->tx_lock);
+ return rc;
+}
+
+static ssize_t hdmi_tx_sysfs_rda_sim_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ struct hdmi_tx_ctrl *hdmi_ctrl =
+ hdmi_tx_get_drvdata_from_sysfs_dev(dev);
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hdmi_ctrl->tx_lock);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", hdmi_ctrl->sim_mode);
+ DEV_DBG("%s: '%d'\n", __func__, hdmi_ctrl->sim_mode);
+ mutex_unlock(&hdmi_ctrl->tx_lock);
+
+ return ret;
+}
+
+static ssize_t hdmi_tx_sysfs_wta_sim_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int sim_mode, rc;
+ struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
+ struct dss_io_data *io = NULL;
+
+ hdmi_ctrl = hdmi_tx_get_drvdata_from_sysfs_dev(dev);
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&hdmi_ctrl->tx_lock);
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ if (!io->base) {
+ DEV_ERR("%s: core io is not initialized\n", __func__);
+ rc = -EINVAL;
goto end;
+ }
- atomic_set(&hdmi_ctrl->audio_ack_pending, 0);
+ if (!hdmi_ctrl->hpd_initialized) {
+ DEV_ERR("%s: hpd not enabled\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ }
- ack_hpd = ack & AUDIO_ACK_CONNECT;
+ rc = kstrtoint(buf, 10, &sim_mode);
+ if (rc) {
+ DEV_ERR("%s: kstrtoint failed. rc=%d\n", __func__, rc);
+ goto end;
+ }
- DEV_DBG("%s: acknowledging %s\n", __func__,
- ack_hpd ? "connect" : "disconnect");
+ hdmi_ctrl->sim_mode = !!sim_mode;
- if (ack_hpd != hdmi_ctrl->hpd_state) {
- DEV_INFO("%s: unbalanced audio state, ack %d, hpd %d\n",
- __func__, ack_hpd, hdmi_ctrl->hpd_state);
+ if (hdmi_ctrl->sim_mode) {
+ DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0));
+ } else {
+ int cable_sense = DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1);
- hdmi_tx_set_audio_switch_node(hdmi_ctrl, hdmi_ctrl->hpd_state);
+ DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0) | BIT(2) |
+ (cable_sense ? 0 : BIT(1)));
}
+
+ rc = strnlen(buf, PAGE_SIZE);
end:
- return ret;
+ mutex_unlock(&hdmi_ctrl->tx_lock);
+ return rc;
}
static ssize_t hdmi_tx_sysfs_rda_video_mode(struct device *dev,
@@ -928,7 +924,10 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev,
goto end;
}
- hdmi_ctrl->audio_ack_enabled = false;
+ /* disable audio ack feature */
+ if (hdmi_ctrl->audio_ops.ack)
+ hdmi_ctrl->audio_ops.ack(hdmi_ctrl->audio_data,
+ AUDIO_ACK_SET_ENABLE, hdmi_ctrl->hpd_state);
if (hdmi_ctrl->panel_power_on) {
hdmi_ctrl->hpd_off_pending = true;
@@ -1312,6 +1311,9 @@ end:
static DEVICE_ATTR(connected, S_IRUGO, hdmi_tx_sysfs_rda_connected, NULL);
static DEVICE_ATTR(hdmi_audio_cb, S_IWUSR, NULL, hdmi_tx_sysfs_wta_audio_cb);
+static DEVICE_ATTR(hot_plug, S_IWUSR, NULL, hdmi_tx_sysfs_wta_hot_plug);
+static DEVICE_ATTR(sim_mode, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_sim_mode,
+ hdmi_tx_sysfs_wta_sim_mode);
static DEVICE_ATTR(edid, S_IRUGO | S_IWUSR, hdmi_tx_sysfs_rda_edid,
hdmi_tx_sysfs_wta_edid);
static DEVICE_ATTR(video_mode, S_IRUGO, hdmi_tx_sysfs_rda_video_mode, NULL);
@@ -1331,6 +1333,8 @@ static DEVICE_ATTR(5v, S_IWUSR, NULL, hdmi_tx_sysfs_wta_5v);
static struct attribute *hdmi_tx_fs_attrs[] = {
&dev_attr_connected.attr,
&dev_attr_hdmi_audio_cb.attr,
+ &dev_attr_hot_plug.attr,
+ &dev_attr_sim_mode.attr,
&dev_attr_edid.attr,
&dev_attr_video_mode.attr,
&dev_attr_hpd.attr,
@@ -1725,6 +1729,7 @@ static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl,
struct hdmi_hdcp_init_data hdcp_init_data = {0};
struct hdmi_cec_init_data cec_init_data = {0};
struct cec_abstract_init_data cec_abst_init_data = {0};
+ struct hdmi_audio_init_data audio_init_data = {0};
struct resource *res = NULL;
void *fd = NULL;
int ret = 0;
@@ -1837,6 +1842,10 @@ static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl,
hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC_ABST] = cec_abst_data;
hdmi_ctrl->panel_data.panel_info.cec_data = cec_abst_data;
+ audio_init_data.io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ audio_init_data.ops = &hdmi_ctrl->audio_ops;
+ hdmi_ctrl->audio_data = hdmi_audio_register(&audio_init_data);
+
return 0;
err_cec_abst:
@@ -1914,20 +1923,23 @@ static int hdmi_tx_init_panel_info(struct hdmi_tx_ctrl *hdmi_ctrl)
static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
{
- int status;
+ int status = 0;
+ void *data;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -EINVAL;
}
+ data = hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID];
+
if (!hdmi_tx_is_controller_on(hdmi_ctrl)) {
DEV_ERR("%s: failed: HDMI controller is off", __func__);
status = -ENXIO;
goto error;
}
- if (!hdmi_ctrl->custom_edid) {
+ if (!hdmi_ctrl->custom_edid && !hdmi_ctrl->sim_mode) {
hdmi_ddc_config(&hdmi_ctrl->ddc_ctrl);
status = hdmi_tx_read_edid(hdmi_ctrl);
@@ -1935,13 +1947,14 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
DEV_ERR("%s: error reading edid\n", __func__);
goto error;
}
- } else {
- hdmi_ctrl->custom_edid = false;
}
- status = hdmi_edid_parser(hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]);
- if (status)
- DEV_ERR("%s: edid parse failed\n", __func__);
+ /* parse edid if a valid edid buffer is present */
+ if (hdmi_ctrl->custom_edid || !hdmi_ctrl->sim_mode) {
+ status = hdmi_edid_parser(data);
+ if (status)
+ DEV_ERR("%s: edid parse failed\n", __func__);
+ }
error:
return status;
@@ -2784,7 +2797,7 @@ static int hdmi_tx_config_power(struct hdmi_tx_ctrl *hdmi_ctrl,
snprintf(name, MAX_CLIENT_NAME_LEN, "hdmi:%u", module);
hdmi_ctrl->pdata.reg_bus_clt[module] =
mdss_reg_bus_vote_client_create(name);
- if (IS_ERR_OR_NULL(hdmi_ctrl->pdata.reg_bus_clt[module])) {
+ if (IS_ERR(hdmi_ctrl->pdata.reg_bus_clt[module])) {
pr_err("reg bus client create failed\n");
msm_dss_config_vreg(&hdmi_ctrl->pdev->dev,
power_data->vreg_config, power_data->num_vreg, 0);
@@ -2917,7 +2930,7 @@ static int hdmi_tx_enable_power(struct hdmi_tx_ctrl *hdmi_ctrl,
goto disable_vreg;
}
mdss_update_reg_bus_vote(hdmi_ctrl->pdata.reg_bus_clt[module],
- VOTE_INDEX_19_MHZ);
+ VOTE_INDEX_LOW);
rc = msm_dss_clk_set_rate(power_data->clk_config,
power_data->num_clk);
@@ -3045,341 +3058,12 @@ static void hdmi_tx_phy_reset(struct hdmi_tx_ctrl *hdmi_ctrl)
DSS_REG_W_ND(io, HDMI_PHY_CTRL, val | SW_RESET_PLL);
} /* hdmi_tx_phy_reset */
-static int hdmi_tx_audio_acr_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
- bool enabled)
-{
- /* Read first before writing */
- u32 acr_pck_ctrl_reg;
- u32 sample_rate_hz;
- u32 pixel_freq;
- struct dss_io_data *io = NULL;
-
- if (!hdmi_ctrl) {
- DEV_ERR("%s: Invalid input\n", __func__);
- return -EINVAL;
- }
-
- sample_rate_hz = hdmi_ctrl->audio_data.sample_rate_hz;
-
- io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
- if (!io->base) {
- DEV_ERR("%s: core io not inititalized\n", __func__);
- return -EINVAL;
- }
-
- acr_pck_ctrl_reg = DSS_REG_R(io, HDMI_ACR_PKT_CTRL);
-
- if (enabled) {
- struct msm_hdmi_mode_timing_info *timing =
- &hdmi_ctrl->vid_cfg.timing;
- const struct hdmi_tx_audio_acr_arry *audio_acr =
- &hdmi_tx_audio_acr_lut[0];
- const int lut_size = sizeof(hdmi_tx_audio_acr_lut)
- / sizeof(*hdmi_tx_audio_acr_lut);
- u32 i, n, cts, layout, multiplier, aud_pck_ctrl_2_reg;
-
- if (timing == NULL) {
- DEV_WARN("%s: video format %d not supported\n",
- __func__, hdmi_ctrl->vid_cfg.vic);
- return -EPERM;
- }
- pixel_freq = hdmi_tx_setup_tmds_clk_rate(hdmi_ctrl);
-
- for (i = 0; i < lut_size;
- audio_acr = &hdmi_tx_audio_acr_lut[++i]) {
- if (audio_acr->pclk == pixel_freq)
- break;
- }
- if (i >= lut_size) {
- DEV_WARN("%s: pixel clk %d not supported\n", __func__,
- pixel_freq);
- return -EPERM;
- }
-
- n = audio_acr->lut[sample_rate_hz].n;
- cts = audio_acr->lut[sample_rate_hz].cts;
- layout = (MSM_HDMI_AUDIO_CHANNEL_2 ==
- hdmi_ctrl->audio_data.num_of_channels) ? 0 : 1;
-
- if (
- (AUDIO_SAMPLE_RATE_192KHZ == sample_rate_hz) ||
- (AUDIO_SAMPLE_RATE_176_4KHZ == sample_rate_hz)) {
- multiplier = 4;
- n >>= 2; /* divide N by 4 and use multiplier */
- } else if (
- (AUDIO_SAMPLE_RATE_96KHZ == sample_rate_hz) ||
- (AUDIO_SAMPLE_RATE_88_2KHZ == sample_rate_hz)) {
- multiplier = 2;
- n >>= 1; /* divide N by 2 and use multiplier */
- } else {
- multiplier = 1;
- }
- DEV_DBG("%s: n=%u, cts=%u, layout=%u\n", __func__, n, cts,
- layout);
-
- /* AUDIO_PRIORITY | SOURCE */
- acr_pck_ctrl_reg |= 0x80000100;
-
- /* Reset multiplier bits */
- acr_pck_ctrl_reg &= ~(7 << 16);
-
- /* N_MULTIPLE(multiplier) */
- acr_pck_ctrl_reg |= (multiplier & 7) << 16;
-
- if ((AUDIO_SAMPLE_RATE_48KHZ == sample_rate_hz) ||
- (AUDIO_SAMPLE_RATE_96KHZ == sample_rate_hz) ||
- (AUDIO_SAMPLE_RATE_192KHZ == sample_rate_hz)) {
- /* SELECT(3) */
- acr_pck_ctrl_reg |= 3 << 4;
- /* CTS_48 */
- cts <<= 12;
-
- /* CTS: need to determine how many fractional bits */
- DSS_REG_W(io, HDMI_ACR_48_0, cts);
- /* N */
- DSS_REG_W(io, HDMI_ACR_48_1, n);
- } else if (
- (AUDIO_SAMPLE_RATE_44_1KHZ == sample_rate_hz) ||
- (AUDIO_SAMPLE_RATE_88_2KHZ == sample_rate_hz) ||
- (AUDIO_SAMPLE_RATE_176_4KHZ == sample_rate_hz)) {
- /* SELECT(2) */
- acr_pck_ctrl_reg |= 2 << 4;
- /* CTS_44 */
- cts <<= 12;
-
- /* CTS: need to determine how many fractional bits */
- DSS_REG_W(io, HDMI_ACR_44_0, cts);
- /* N */
- DSS_REG_W(io, HDMI_ACR_44_1, n);
- } else { /* default to 32k */
- /* SELECT(1) */
- acr_pck_ctrl_reg |= 1 << 4;
- /* CTS_32 */
- cts <<= 12;
-
- /* CTS: need to determine how many fractional bits */
- DSS_REG_W(io, HDMI_ACR_32_0, cts);
- /* N */
- DSS_REG_W(io, HDMI_ACR_32_1, n);
- }
- /* Payload layout depends on number of audio channels */
- /* LAYOUT_SEL(layout) */
- aud_pck_ctrl_2_reg = 1 | (layout << 1);
- /* override | layout */
- DSS_REG_W(io, HDMI_AUDIO_PKT_CTRL2, aud_pck_ctrl_2_reg);
-
- /* SEND | CONT */
- acr_pck_ctrl_reg |= 0x00000003;
- } else {
- /* ~(SEND | CONT) */
- acr_pck_ctrl_reg &= ~0x00000003;
- }
- DSS_REG_W(io, HDMI_ACR_PKT_CTRL, acr_pck_ctrl_reg);
-
- return 0;
-} /* hdmi_tx_audio_acr_setup */
-
-static int hdmi_tx_audio_iframe_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
- bool enabled)
-{
- struct dss_io_data *io = NULL;
-
- u32 hdmi_debug_reg = 0;
- u32 channel_count = 1; /* Def to 2 channels -> Table 17 in CEA-D */
- u32 num_of_channels;
- u32 channel_allocation;
- u32 level_shift;
- u32 down_mix;
- u32 check_sum, audio_info_0_reg, audio_info_1_reg;
- u32 audio_info_ctrl_reg;
- u32 aud_pck_ctrl_2_reg;
- u32 layout;
- u32 sample_present;
-
- if (!hdmi_ctrl) {
- DEV_ERR("%s: invalid input\n", __func__);
- return -EINVAL;
- }
-
- num_of_channels = hdmi_ctrl->audio_data.num_of_channels;
- channel_allocation = hdmi_ctrl->audio_data.channel_allocation;
- level_shift = hdmi_ctrl->audio_data.level_shift;
- down_mix = hdmi_ctrl->audio_data.down_mix;
- sample_present = hdmi_ctrl->audio_data.sample_present;
-
- io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
- if (!io->base) {
- DEV_ERR("%s: core io not inititalized\n", __func__);
- return -EINVAL;
- }
-
- layout = (MSM_HDMI_AUDIO_CHANNEL_2 == num_of_channels) ? 0 : 1;
- aud_pck_ctrl_2_reg = 1 | (layout << 1);
- DSS_REG_W(io, HDMI_AUDIO_PKT_CTRL2, aud_pck_ctrl_2_reg);
-
- /*
- * Please see table 20 Audio InfoFrame in HDMI spec
- * FL = front left
- * FC = front Center
- * FR = front right
- * FLC = front left center
- * FRC = front right center
- * RL = rear left
- * RC = rear center
- * RR = rear right
- * RLC = rear left center
- * RRC = rear right center
- * LFE = low frequency effect
- */
-
- /* Read first then write because it is bundled with other controls */
- audio_info_ctrl_reg = DSS_REG_R(io, HDMI_INFOFRAME_CTRL0);
-
- if (enabled) {
- switch (num_of_channels) {
- case MSM_HDMI_AUDIO_CHANNEL_2:
- break;
- case MSM_HDMI_AUDIO_CHANNEL_3:
- channel_count = 2;
- break;
- case MSM_HDMI_AUDIO_CHANNEL_4:
- channel_count = 3;
- break;
- case MSM_HDMI_AUDIO_CHANNEL_5:
- channel_count = 4;
- break;
- case MSM_HDMI_AUDIO_CHANNEL_6:
- channel_count = 5;
- break;
- case MSM_HDMI_AUDIO_CHANNEL_7:
- channel_count = 6;
- break;
- case MSM_HDMI_AUDIO_CHANNEL_8:
- channel_count = 7;
- break;
- default:
- DEV_ERR("%s: Unsupported num_of_channels = %u\n",
- __func__, num_of_channels);
- return -EINVAL;
- }
-
- /* Program the Channel-Speaker allocation */
- audio_info_1_reg = 0;
- /* CA(channel_allocation) */
- audio_info_1_reg |= channel_allocation & 0xff;
- /* Program the Level shifter */
- audio_info_1_reg |= (level_shift << 11) & 0x00007800;
- /* Program the Down-mix Inhibit Flag */
- audio_info_1_reg |= (down_mix << 15) & 0x00008000;
-
- DSS_REG_W(io, HDMI_AUDIO_INFO1, audio_info_1_reg);
-
- /*
- * Calculate CheckSum: Sum of all the bytes in the
- * Audio Info Packet (See table 8.4 in HDMI spec)
- */
- check_sum = 0;
- /* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_TYPE[0x84] */
- check_sum += 0x84;
- /* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_VERSION[0x01] */
- check_sum += 1;
- /* HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH[0x0A] */
- check_sum += 0x0A;
- check_sum += channel_count;
- check_sum += channel_allocation;
- /* See Table 8.5 in HDMI spec */
- check_sum += (level_shift & 0xF) << 3 | (down_mix & 0x1) << 7;
- check_sum &= 0xFF;
- check_sum = (u8) (256 - check_sum);
-
- audio_info_0_reg = 0;
- /* CHECKSUM(check_sum) */
- audio_info_0_reg |= check_sum & 0xff;
- /* CC(channel_count) */
- audio_info_0_reg |= (channel_count << 8) & 0x00000700;
-
- DSS_REG_W(io, HDMI_AUDIO_INFO0, audio_info_0_reg);
-
- /*
- * Set these flags
- * AUDIO_INFO_UPDATE |
- * AUDIO_INFO_SOURCE |
- * AUDIO_INFO_CONT |
- * AUDIO_INFO_SEND
- */
- audio_info_ctrl_reg |= 0x000000F0;
-
- /*
- * Program the Sample Present into the debug register so that
- * the HDMI transmitter core can add the sample present to
- * Audio Sample Packet once tranmission starts.
- */
- if (layout) {
- /* Set the Layout bit */
- hdmi_debug_reg |= BIT(4);
- /* Set the Sample Present bits */
- hdmi_debug_reg |= sample_present & 0xF;
- DSS_REG_W(io, HDMI_DEBUG, hdmi_debug_reg);
- }
- } else {
- /*Clear these flags
- * ~(AUDIO_INFO_UPDATE |
- * AUDIO_INFO_SOURCE |
- * AUDIO_INFO_CONT |
- * AUDIO_INFO_SEND)
- */
- audio_info_ctrl_reg &= ~0x000000F0;
- }
- DSS_REG_W(io, HDMI_INFOFRAME_CTRL0, audio_info_ctrl_reg);
-
- dss_reg_dump(io->base, io->len,
- enabled ? "HDMI-AUDIO-ON: " : "HDMI-AUDIO-OFF: ", REG_DUMP);
-
- return 0;
-} /* hdmi_tx_audio_iframe_setup */
-
-static int hdmi_tx_get_audio_sample_rate(u32 *sample_rate_hz)
-{
- int ret = 0;
- u32 rate = *sample_rate_hz;
-
- switch (rate) {
- case 32000:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_32KHZ;
- break;
- case 44100:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_44_1KHZ;
- break;
- case 48000:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_48KHZ;
- break;
- case 88200:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_88_2KHZ;
- break;
- case 96000:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_96KHZ;
- break;
- case 176400:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_176_4KHZ;
- break;
- case 192000:
- *sample_rate_hz = AUDIO_SAMPLE_RATE_192KHZ;
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-} /* hdmi_tx_get_audio_sample_rate */
-
static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
struct msm_hdmi_audio_setup_params *params)
{
int rc = 0;
struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev);
u32 is_mode_dvi;
- u32 *sample_rate_hz;
if (!hdmi_ctrl || !params) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -3391,38 +3075,31 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev,
is_mode_dvi = hdmi_tx_is_dvi_mode(hdmi_ctrl);
if (!is_mode_dvi && hdmi_tx_is_panel_on(hdmi_ctrl)) {
- memcpy(&hdmi_ctrl->audio_data, params,
+ memcpy(&hdmi_ctrl->audio_params, params,
sizeof(struct msm_hdmi_audio_setup_params));
- sample_rate_hz = &hdmi_ctrl->audio_data.sample_rate_hz;
- rc = hdmi_tx_get_audio_sample_rate(sample_rate_hz);
- if (rc) {
- DEV_ERR("%s: invalid sample rate = %d\n",
- __func__, hdmi_ctrl->audio_data.sample_rate_hz);
- goto exit;
- }
-
- rc = hdmi_tx_audio_setup(hdmi_ctrl);
- if (rc)
- DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.rc=%d\n",
- __func__, rc);
+ hdmi_tx_audio_setup(hdmi_ctrl);
} else {
rc = -EPERM;
}
- if (rc)
+ if (rc) {
+ struct hdmi_audio_status status = {0};
+
+ if (hdmi_ctrl->audio_ops.status)
+ hdmi_ctrl->audio_ops.status(hdmi_ctrl->audio_data,
+ &status);
+
dev_err_ratelimited(&hdmi_ctrl->pdev->dev,
"%s: hpd %d, ack %d, switch %d, mode %s, power %d\n",
__func__, hdmi_ctrl->hpd_state,
- atomic_read(&hdmi_ctrl->audio_ack_pending),
- hdmi_ctrl->audio_sdev.state,
+ status.ack_pending, status.switched,
is_mode_dvi ? "dvi" : "hdmi",
hdmi_ctrl->panel_power_on);
-
-exit:
+ }
mutex_unlock(&hdmi_ctrl->tx_lock);
return rc;
-} /* hdmi_tx_audio_info_setup */
+}
static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev,
struct msm_hdmi_audio_edid_blk *blk)
@@ -3434,9 +3111,6 @@ static int hdmi_tx_get_audio_edid_blk(struct platform_device *pdev,
return -ENODEV;
}
- if (!hdmi_ctrl->audio_sdev.state)
- return -EPERM;
-
return hdmi_edid_get_audio_blk(
hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID], blk);
} /* hdmi_tx_get_audio_edid_blk */
@@ -3524,13 +3198,19 @@ static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote)
* consider this as an error as it will result in whole
* audio path to fail.
*/
- if (!hpd)
+ if (!hpd) {
+ struct hdmi_audio_status status = {0};
+
+ if (hdmi_ctrl->audio_ops.status)
+ hdmi_ctrl->audio_ops.status(hdmi_ctrl->audio_data,
+ &status);
+
dev_err_ratelimited(&hdmi_ctrl->pdev->dev,
"%s: hpd %d, ack %d, switch %d, power %d\n",
__func__, hdmi_ctrl->hpd_state,
- atomic_read(&hdmi_ctrl->audio_ack_pending),
- hdmi_ctrl->audio_sdev.state,
+ status.ack_pending, status.switched,
hdmi_ctrl->panel_power_on);
+ }
return hpd;
}
@@ -3553,68 +3233,6 @@ int msm_hdmi_register_audio_codec(struct platform_device *pdev,
} /* hdmi_tx_audio_register */
EXPORT_SYMBOL(msm_hdmi_register_audio_codec);
-static int hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl)
-{
- int rc = 0;
- struct dss_io_data *io = NULL;
-
- if (!hdmi_ctrl) {
- DEV_ERR("%s: invalid input\n", __func__);
- return -EINVAL;
- }
-
- io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
- if (!io->base) {
- DEV_ERR("%s: core io not inititalized\n", __func__);
- return -EINVAL;
- }
-
- rc = hdmi_tx_audio_acr_setup(hdmi_ctrl, true);
- if (rc) {
- DEV_ERR("%s: hdmi_tx_audio_acr_setup failed. rc=%d\n",
- __func__, rc);
- return rc;
- }
-
- rc = hdmi_tx_audio_iframe_setup(hdmi_ctrl, true);
- if (rc) {
- DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed. rc=%d\n",
- __func__, rc);
- return rc;
- }
-
- DEV_INFO("HDMI Audio: Enabled\n");
-
- return 0;
-} /* hdmi_tx_audio_setup */
-
-static void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
-{
- struct dss_io_data *io = NULL;
-
- if (!hdmi_ctrl) {
- DEV_ERR("%s: invalid input\n", __func__);
- return;
- }
-
- io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
- if (!io->base) {
- DEV_ERR("%s: core io not inititalized\n", __func__);
- return;
- }
-
- if (hdmi_tx_audio_iframe_setup(hdmi_ctrl, false))
- DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.\n", __func__);
-
- if (hdmi_tx_audio_acr_setup(hdmi_ctrl, false))
- DEV_ERR("%s: hdmi_tx_audio_acr_setup failed.\n", __func__);
-
- hdmi_ctrl->audio_data.sample_rate_hz = AUDIO_SAMPLE_RATE_48KHZ;
- hdmi_ctrl->audio_data.num_of_channels = MSM_HDMI_AUDIO_CHANNEL_2;
-
- DEV_INFO("HDMI Audio: Disabled\n");
-} /* hdmi_tx_audio_off */
-
static int hdmi_tx_setup_tmds_clk_rate(struct hdmi_tx_ctrl *hdmi_ctrl)
{
u32 rate = 0;
@@ -3824,6 +3442,11 @@ static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
return;
}
+ if (hdmi_ctrl->sim_mode) {
+ DEV_DBG("%s: sim mode enabled\n", __func__);
+ return;
+ }
+
if (polarity)
DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(2) | BIT(1));
else
@@ -3843,6 +3466,15 @@ static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl,
}
} /* hdmi_tx_hpd_polarity_setup */
+static inline void hdmi_tx_audio_off(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ if (hdmi_ctrl && hdmi_ctrl->audio_ops.off)
+ hdmi_ctrl->audio_ops.off(hdmi_ctrl->audio_data);
+
+ memset(&hdmi_ctrl->audio_params, 0,
+ sizeof(struct msm_hdmi_audio_setup_params));
+}
+
static int hdmi_tx_power_off(struct mdss_panel_data *panel_data)
{
struct dss_io_data *io = NULL;
@@ -4077,8 +3709,6 @@ static int hdmi_tx_hpd_on(struct hdmi_tx_ctrl *hdmi_ctrl)
/* Turn on HPD HW circuit */
DSS_REG_W(io, HDMI_HPD_CTRL, reg_val | BIT(28));
- atomic_set(&hdmi_ctrl->audio_ack_pending, 0);
-
hdmi_tx_hpd_polarity_setup(hdmi_ctrl, HPD_CONNECT_POLARITY);
DEV_DBG("%s: HPD is now ON\n", __func__);
}
@@ -4268,7 +3898,6 @@ static void hdmi_tx_dev_deinit(struct hdmi_tx_ctrl *hdmi_ctrl)
hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID] = NULL;
}
- switch_dev_unregister(&hdmi_ctrl->audio_sdev);
switch_dev_unregister(&hdmi_ctrl->sdev);
if (hdmi_ctrl->workq)
destroy_workqueue(hdmi_ctrl->workq);
@@ -4328,9 +3957,6 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl)
spin_lock_init(&hdmi_ctrl->hpd_state_lock);
- hdmi_ctrl->audio_data.sample_rate_hz = AUDIO_SAMPLE_RATE_48KHZ;
- hdmi_ctrl->audio_data.num_of_channels = MSM_HDMI_AUDIO_CHANNEL_2;
-
return 0;
fail_create_workq:
@@ -4379,12 +4005,6 @@ static int hdmi_tx_init_switch_dev(struct hdmi_tx_ctrl *hdmi_ctrl)
DEV_ERR("%s: display switch registration failed\n", __func__);
goto end;
}
-
- hdmi_ctrl->audio_sdev.name = "hdmi_audio";
- rc = switch_dev_register(&hdmi_ctrl->audio_sdev);
- if (rc)
- DEV_ERR("%s: audio switch registration failed\n", __func__);
-
end:
return rc;
}
@@ -4545,11 +4165,14 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data,
break;
case MDSS_EVENT_PANEL_ON:
- hdmi_tx_update_hdcp_info(hdmi_ctrl);
+ if (!hdmi_ctrl->sim_mode) {
+ hdmi_tx_update_hdcp_info(hdmi_ctrl);
- rc = hdmi_tx_start_hdcp(hdmi_ctrl);
- if (rc)
- DEV_ERR("%s: hdcp start failed rc=%d\n", __func__, rc);
+ rc = hdmi_tx_start_hdcp(hdmi_ctrl);
+ if (rc)
+ DEV_ERR("%s: hdcp start failed rc=%d\n",
+ __func__, rc);
+ }
hdmi_ctrl->timing_gen_on = true;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h
index 34474ecf0ff0..170837dcc9e6 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h
@@ -16,6 +16,7 @@
#include <linux/switch.h>
#include "mdss_hdmi_util.h"
#include "mdss_cec_core.h"
+#include "mdss_hdmi_audio.h"
#define MAX_SWITCH_NAME_SIZE 5
@@ -130,14 +131,12 @@ struct hdmi_tx_ctrl {
struct hdmi_tx_pinctrl pin_res;
- struct msm_hdmi_audio_setup_params audio_data;
struct mutex mutex;
struct mutex tx_lock;
struct list_head cable_notify_handlers;
struct kobject *kobj;
struct switch_dev sdev;
- struct switch_dev audio_sdev;
struct workqueue_struct *workq;
spinlock_t hpd_state_lock;
@@ -168,11 +167,10 @@ struct hdmi_tx_ctrl {
bool scrambler_enabled;
u32 hdcp14_present;
bool hdcp1_use_sw_keys;
- bool audio_ack_enabled;
- atomic_t audio_ack_pending;
bool hdcp14_sw_keys;
bool auth_state;
bool custom_edid;
+ bool sim_mode;
u32 enc_lvl;
u8 spd_vendor_name[9];
@@ -182,6 +180,7 @@ struct hdmi_tx_ctrl {
void (*hdmi_tx_hpd_done) (void *data);
void *downstream_data;
+ void *audio_data;
void *feature_data[HDMI_TX_FEAT_MAX];
struct hdmi_hdcp_ops *hdcp_ops;
@@ -194,6 +193,8 @@ struct hdmi_tx_ctrl {
struct cec_ops hdmi_cec_ops;
struct cec_cbs hdmi_cec_cbs;
+ struct hdmi_audio_ops audio_ops;
+ struct msm_hdmi_audio_setup_params audio_params;
char disp_switch_name[MAX_SWITCH_NAME_SIZE];
bool power_data_enable[HDMI_TX_MAX_PM];
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index 33f086dcf62f..4640c3f505b6 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c
@@ -725,10 +725,9 @@ static void hdmi_ddc_trigger(struct hdmi_tx_ddc_ctrl *ddc_ctrl,
DSS_REG_W_ND(io, HDMI_DDC_CTRL, ddc_ctrl_reg_val);
}
-static int hdmi_ddc_check_status(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
+static void hdmi_ddc_clear_status(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
u32 reg_val;
- int rc = 0;
/* Read DDC status */
reg_val = DSS_REG_R(ddc_ctrl->io, HDMI_DDC_SW_STATUS);
@@ -743,18 +742,14 @@ static int hdmi_ddc_check_status(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
reg_val = BIT(3) | BIT(1);
DSS_REG_W_ND(ddc_ctrl->io, HDMI_DDC_CTRL, reg_val);
-
- rc = -ECOMM;
}
-
- return rc;
}
static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
u32 reg_val, ndx, time_out_count, wait_time;
struct hdmi_tx_ddc_data *ddc_data;
- int status, rc;
+ int status;
int busy_wait_us;
if (!ddc_ctrl || !ddc_ctrl->io) {
@@ -823,10 +818,7 @@ static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
status = -ETIMEDOUT;
}
- rc = hdmi_ddc_check_status(ddc_ctrl);
-
- if (!status)
- status = rc;
+ hdmi_ddc_clear_status(ddc_ctrl);
} while (status && ddc_data->retry--);
if (status)
@@ -1165,7 +1157,7 @@ int hdmi_ddc_read(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
- int status, rc;
+ int status;
u32 reg_val, ndx, time_out_count;
struct hdmi_tx_ddc_data *ddc_data;
@@ -1206,10 +1198,7 @@ int hdmi_ddc_read_seg(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
status = -ETIMEDOUT;
}
- rc = hdmi_ddc_check_status(ddc_ctrl);
-
- if (!status)
- status = rc;
+ hdmi_ddc_clear_status(ddc_ctrl);
} while (status && ddc_data->retry--);
if (status)
@@ -1235,7 +1224,7 @@ error:
int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
{
- int status, rc;
+ int status;
u32 time_out_count;
struct hdmi_tx_ddc_data *ddc_data;
u32 wait_time;
@@ -1307,10 +1296,7 @@ int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl)
status = -ETIMEDOUT;
}
- rc = hdmi_ddc_check_status(ddc_ctrl);
-
- if (!status)
- status = rc;
+ hdmi_ddc_clear_status(ddc_ctrl);
} while (status && ddc_data->retry--);
if (status)
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 55c98e75755f..b50e29c0e610 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -40,6 +40,8 @@
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/clk/msm-clk.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
@@ -64,6 +66,7 @@
#define RES_UHD (3840*2160)
struct mdss_data_type *mdss_res;
+static u32 mem_protect_sd_ctrl_id;
static int mdss_fb_mem_get_iommu_domain(void)
{
@@ -78,10 +81,6 @@ struct msm_mdp_interface mdp5 = {
.get_format_params = mdss_mdp_get_format_params,
};
-#define DEFAULT_TOTAL_RGB_PIPES 3
-#define DEFAULT_TOTAL_VIG_PIPES 3
-#define DEFAULT_TOTAL_DMA_PIPES 2
-
#define IB_QUOTA 2000000000
#define AB_QUOTA 2000000000
@@ -108,6 +107,14 @@ struct mdss_hw mdss_mdp_hw = {
.irq_handler = mdss_mdp_isr,
};
+/* define for h/w block with external driver */
+struct mdss_hw mdss_misc_hw = {
+ .hw_ndx = MDSS_HW_MISC,
+ .ptr = NULL,
+ .irq_handler = NULL,
+};
+
+#ifdef CONFIG_MSM_BUS_SCALING
#define MDP_REG_BUS_VECTOR_ENTRY(ab_val, ib_val) \
{ \
.src = MSM_BUS_MASTER_AMPSS_M0, \
@@ -134,6 +141,7 @@ static struct msm_bus_scale_pdata mdp_reg_bus_scale_table = {
.name = "mdss_reg",
.active_only = true,
};
+#endif
u32 invalid_mdp107_wb_output_fmts[] = {
MDP_XRGB_8888,
@@ -174,6 +182,61 @@ u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp)
return xres * bpp;
}
+static void mdss_irq_mask(struct irq_data *data)
+{
+ struct mdss_data_type *mdata = irq_data_get_irq_chip_data(data);
+ unsigned long irq_flags;
+
+ if (!mdata)
+ return;
+
+ pr_debug("irq_domain_mask %lu\n", data->hwirq);
+
+ if (data->hwirq < 32) {
+ spin_lock_irqsave(&mdp_lock, irq_flags);
+ mdata->mdss_util->disable_irq(&mdss_misc_hw);
+ spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ }
+}
+
+static void mdss_irq_unmask(struct irq_data *data)
+{
+ struct mdss_data_type *mdata = irq_data_get_irq_chip_data(data);
+ unsigned long irq_flags;
+
+ if (!mdata)
+ return;
+
+ pr_debug("irq_domain_unmask %lu\n", data->hwirq);
+
+ if (data->hwirq < 32) {
+ spin_lock_irqsave(&mdp_lock, irq_flags);
+ mdata->mdss_util->enable_irq(&mdss_misc_hw);
+ spin_unlock_irqrestore(&mdp_lock, irq_flags);
+ }
+}
+
+static struct irq_chip mdss_irq_chip = {
+ .name = "mdss",
+ .irq_mask = mdss_irq_mask,
+ .irq_unmask = mdss_irq_unmask,
+};
+
+static int mdss_irq_domain_map(struct irq_domain *d,
+ unsigned int virq, irq_hw_number_t hw)
+{
+ struct mdss_data_type *mdata = d->host_data;
+ /* check here if virq is a valid interrupt line */
+ irq_set_chip_and_handler(virq, &mdss_irq_chip, handle_level_irq);
+ irq_set_chip_data(virq, mdata);
+ return 0;
+}
+
+static struct irq_domain_ops mdss_irq_domain_ops = {
+ .map = mdss_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
static irqreturn_t mdss_irq_handler(int irq, void *ptr)
{
struct mdss_data_type *mdata = ptr;
@@ -192,25 +255,44 @@ static irqreturn_t mdss_irq_handler(int irq, void *ptr)
spin_lock(&mdp_lock);
mdata->mdss_util->irq_dispatch(MDSS_HW_MDP, irq, ptr);
spin_unlock(&mdp_lock);
+ intr &= ~MDSS_INTR_MDP;
}
- if (intr & MDSS_INTR_DSI0)
+ if (intr & MDSS_INTR_DSI0) {
mdata->mdss_util->irq_dispatch(MDSS_HW_DSI0, irq, ptr);
+ intr &= ~MDSS_INTR_DSI0;
+ }
- if (intr & MDSS_INTR_DSI1)
+ if (intr & MDSS_INTR_DSI1) {
mdata->mdss_util->irq_dispatch(MDSS_HW_DSI1, irq, ptr);
+ intr &= ~MDSS_INTR_DSI1;
+ }
- if (intr & MDSS_INTR_EDP)
+ if (intr & MDSS_INTR_EDP) {
mdata->mdss_util->irq_dispatch(MDSS_HW_EDP, irq, ptr);
+ intr &= ~MDSS_INTR_EDP;
+ }
- if (intr & MDSS_INTR_HDMI)
+ if (intr & MDSS_INTR_HDMI) {
mdata->mdss_util->irq_dispatch(MDSS_HW_HDMI, irq, ptr);
+ intr &= ~MDSS_INTR_HDMI;
+ }
+
+ /* route misc. interrupts to external drivers */
+ while (intr) {
+ irq_hw_number_t hwirq = fls(intr) - 1;
+
+ generic_handle_irq(irq_find_mapping(
+ mdata->irq_domain, hwirq));
+ intr &= ~(1 << hwirq);
+ }
mdss_mdp_hw.irq_info->irq_buzy = false;
return IRQ_HANDLED;
}
+#ifdef CONFIG_MSM_BUS_SCALING
static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata)
{
struct msm_bus_scale_pdata *reg_bus_pdata;
@@ -227,20 +309,24 @@ static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata)
pr_debug("register bus_hdl=%x\n", mdata->bus_hdl);
}
- if (!mdata->reg_bus_hdl) {
+ if (!mdata->reg_bus_scale_table) {
reg_bus_pdata = &mdp_reg_bus_scale_table;
for (i = 0; i < reg_bus_pdata->num_usecases; i++) {
mdp_reg_bus_usecases[i].num_paths = 1;
mdp_reg_bus_usecases[i].vectors =
&mdp_reg_bus_vectors[i];
}
+ mdata->reg_bus_scale_table = reg_bus_pdata;
+ }
+ if (!mdata->reg_bus_hdl) {
mdata->reg_bus_hdl =
- msm_bus_scale_register_client(reg_bus_pdata);
- if (!mdata->reg_bus_hdl) {
+ msm_bus_scale_register_client(
+ mdata->reg_bus_scale_table);
+ if (!mdata->reg_bus_hdl)
/* Continue without reg_bus scaling */
pr_warn("reg_bus_client register failed\n");
- } else
+ else
pr_debug("register reg_bus_hdl=%x\n",
mdata->reg_bus_hdl);
}
@@ -484,6 +570,42 @@ int mdss_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota)
return rc;
}
+#else
+static int mdss_mdp_bus_scale_register(struct mdss_data_type *mdata)
+{
+ return 0;
+}
+
+static void mdss_mdp_bus_scale_unregister(struct mdss_data_type *mdata)
+{
+}
+
+int mdss_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota)
+{
+ pr_debug("No bus scaling! client=%d ab=%llu ib=%llu\n",
+ client, ab_quota, ib_quota);
+
+ return 0;
+}
+
+struct reg_bus_client *mdss_reg_bus_vote_client_create(char *client_name)
+{
+ return NULL;
+}
+
+void mdss_reg_bus_vote_client_destroy(struct reg_bus_client *client)
+{
+}
+
+int mdss_update_reg_bus_vote(struct reg_bus_client *bus_client, u32 usecase_ndx)
+{
+ pr_debug("%pS: No reg scaling! usecase=%u\n",
+ __builtin_return_address(0), usecase_ndx);
+
+ return 0;
+}
+#endif
+
static inline u32 mdss_mdp_irq_mask(u32 intr_type, u32 intf_num)
{
@@ -746,7 +868,7 @@ static inline void __mdss_mdp_reg_access_clk_enable(
{
if (enable) {
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
- VOTE_INDEX_19_MHZ);
+ VOTE_INDEX_LOW);
if (mdss_has_quirk(mdata, MDSS_QUIRK_MIN_BUS_VOTE))
mdss_bus_scale_set_quota(MDSS_HW_RT,
SZ_1M, SZ_1M);
@@ -1029,7 +1151,7 @@ void mdss_mdp_clk_ctrl(int enable)
pm_runtime_get_sync(&mdata->pdev->dev);
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
- VOTE_INDEX_19_MHZ);
+ VOTE_INDEX_LOW);
rc = mdss_iommu_ctrl(1);
if (IS_ERR_VALUE(rc))
@@ -1184,7 +1306,7 @@ static int mdss_mdp_irq_clk_setup(struct mdss_data_type *mdata)
}
mdata->reg_bus_clt = mdss_reg_bus_vote_client_create("mdp\0");
- if (IS_ERR_OR_NULL(mdata->reg_bus_clt)) {
+ if (IS_ERR(mdata->reg_bus_clt)) {
pr_err("bus client register failed\n");
return PTR_ERR(mdata->reg_bus_clt);
}
@@ -1274,9 +1396,10 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->hflip_buffer_reused = true;
/* prevent disable of prefill calculations */
mdata->min_prefill_lines = 0xffff;
- /* clock gating feature is disabled by default */
+ /* clock gating feature is enabled by default */
mdata->enable_gate = true;
mdata->pixel_ram_size = 0;
+ mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL_FLAT;
mdss_mdp_hw_rev_debug_caps_init(mdata);
@@ -1336,6 +1459,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_OTLIM, mdata->mdss_qos_map);
break;
case MDSS_MDP_HW_REV_114:
+ /* disable ECG for 28nm PHY platform */
+ mdata->enable_gate = false;
case MDSS_MDP_HW_REV_116:
mdata->max_target_zorder = 4; /* excluding base layer */
mdata->max_cursor_size = 128;
@@ -1344,6 +1469,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->pixel_ram_size = 40 * 1024;
mdata->apply_post_scale_bytes = false;
mdata->hflip_buffer_reused = false;
+ mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL;
set_bit(MDSS_QOS_OVERHEAD_FACTOR, mdata->mdss_qos_map);
set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map);
set_bit(MDSS_QOS_PER_PIPE_LUT, mdata->mdss_qos_map);
@@ -1353,6 +1479,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_OTLIM, mdata->mdss_qos_map);
mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR);
mdss_set_quirk(mdata, MDSS_QUIRK_MIN_BUS_VOTE);
+ mdss_set_quirk(mdata, MDSS_QUIRK_NEED_SECURE_MAP);
break;
case MDSS_MDP_HW_REV_115:
mdata->max_target_zorder = 4; /* excluding base layer */
@@ -1362,6 +1489,9 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdata->pixel_ram_size = 16 * 1024;
mdata->apply_post_scale_bytes = false;
mdata->hflip_buffer_reused = false;
+ /* disable ECG for 28nm PHY platform */
+ mdata->enable_gate = false;
+ mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL;
set_bit(MDSS_QOS_CDP, mdata->mdss_qos_map);
set_bit(MDSS_QOS_PER_PIPE_LUT, mdata->mdss_qos_map);
set_bit(MDSS_QOS_SIMPLIFIED_PREFILL, mdata->mdss_qos_map);
@@ -1371,6 +1501,7 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_OTLIM, mdata->mdss_qos_map);
mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR);
mdss_set_quirk(mdata, MDSS_QUIRK_MIN_BUS_VOTE);
+ mdss_set_quirk(mdata, MDSS_QUIRK_NEED_SECURE_MAP);
break;
case MDSS_MDP_HW_REV_300:
case MDSS_MDP_HW_REV_301:
@@ -1436,8 +1567,6 @@ static void mdss_hw_rev_init(struct mdss_data_type *mdata)
*/
void mdss_hw_init(struct mdss_data_type *mdata)
{
- int i, j;
- char *offset;
struct mdss_mdp_pipe *vig;
mdss_hw_rev_init(mdata);
@@ -1456,28 +1585,7 @@ void mdss_hw_init(struct mdss_data_type *mdata)
}
}
- for (i = 0; i < mdata->ndspp; i++) {
- offset = mdata->mixer_intf[i].dspp_base +
- MDSS_MDP_REG_DSPP_HIST_LUT_BASE;
- for (j = 0; j < ENHIST_LUT_ENTRIES; j++)
- writel_relaxed(j, offset);
-
- /* swap */
- writel_relaxed(1, offset + 4);
- }
vig = mdata->vig_pipes;
- for (i = 0; i < mdata->nvig_pipes; i++) {
- offset = vig[i].base +
- MDSS_MDP_REG_VIG_HIST_LUT_BASE;
- for (j = 0; j < ENHIST_LUT_ENTRIES; j++)
- writel_relaxed(j, offset);
- /* swap */
- writel_relaxed(1, offset + 16);
- }
-
- /* initialize csc matrix default value */
- for (i = 0; i < mdata->nvig_pipes; i++)
- vig[i].csc_coeff_set = MDP_CSC_ITU_R_709;
mdata->nmax_concurrent_ad_hw =
(mdata->mdp_rev < MDSS_MDP_HW_REV_103) ? 1 : 2;
@@ -1767,11 +1875,11 @@ static int mdss_mdp_get_cmdline_config(struct platform_device *pdev)
rc = mdss_mdp_parse_dt_pan_intf(pdev);
/* if pref pan intf is not present */
if (rc)
- pr_err("unable to parse device tree for pan intf\n");
- else
- pan_cfg->init_done = true;
+ pr_warn("unable to parse device tree for pan intf\n");
- return rc;
+ pan_cfg->init_done = true;
+
+ return 0;
}
static void __update_sspp_info(struct mdss_mdp_pipe *pipe,
@@ -2121,6 +2229,20 @@ static int mdss_mdp_probe(struct platform_device *pdev)
mdss_mdp_hw.irq_info->irq = res->start;
mdss_mdp_hw.ptr = mdata;
+ /* export misc. interrupts to external driver */
+ mdata->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 32,
+ &mdss_irq_domain_ops, mdata);
+ if (!mdata->irq_domain) {
+ pr_err("unable to add linear domain\n");
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+
+ mdss_misc_hw.irq_info = mdss_intr_line();
+ rc = mdss_res->mdss_util->register_irq(&mdss_misc_hw);
+ if (rc)
+ pr_err("mdss_register_irq failed.\n");
+
/*populate hw iomem base info from device tree*/
rc = mdss_mdp_parse_dt(pdev);
if (rc) {
@@ -2139,11 +2261,6 @@ static int mdss_mdp_probe(struct platform_device *pdev)
pr_err("unable to initialize mdss mdp resources\n");
goto probe_done;
}
- rc = mdss_mdp_pp_init(&pdev->dev);
- if (rc) {
- pr_err("unable to initialize mdss pp resources\n");
- goto probe_done;
- }
rc = mdss_mdp_bus_scale_register(mdata);
if (rc) {
pr_err("unable to register bus scaling\n");
@@ -2200,6 +2317,10 @@ static int mdss_mdp_probe(struct platform_device *pdev)
mdss_mdp_footswitch_ctrl_splash(true);
mdss_hw_init(mdata);
+ rc = mdss_mdp_pp_init(&pdev->dev);
+ if (rc)
+ pr_err("unable to initialize mdss pp resources\n");
+
/* Restoring Secure configuration during boot-up */
if (mdss_mdp_req_init_restore_cfg(mdata))
__mdss_restore_sec_cfg(mdata);
@@ -2268,7 +2389,7 @@ int mdss_mdp_parse_dt_hw_settings(struct platform_device *pdev)
vbif_arr = of_get_property(pdev->dev.of_node, "qcom,vbif-settings",
&vbif_len);
if (!vbif_arr || (vbif_len & 1)) {
- pr_warn("MDSS VBIF settings not found\n");
+ pr_debug("MDSS VBIF settings not found\n");
vbif_len = 0;
}
vbif_len /= 2 * sizeof(u32);
@@ -2284,7 +2405,7 @@ int mdss_mdp_parse_dt_hw_settings(struct platform_device *pdev)
mdp_arr = of_get_property(pdev->dev.of_node, "qcom,mdp-settings",
&mdp_len);
if (!mdp_arr || (mdp_len & 1)) {
- pr_warn("MDSS MDP settings not found\n");
+ pr_debug("MDSS MDP settings not found\n");
mdp_len = 0;
}
mdp_len /= 2 * sizeof(u32);
@@ -2512,267 +2633,86 @@ static void mdss_mdp_parse_dt_pipe_panic_ctrl(struct platform_device *pdev,
}
static int mdss_mdp_parse_dt_pipe_helper(struct platform_device *pdev,
- u32 npipes,
- u32 nfids)
+ u32 ptype, char *ptypestr,
+ struct mdss_mdp_pipe **out_plist,
+ size_t len,
+ u8 priority_base)
{
- u32 dma_off;
- u32 *offsets = NULL, *ftch_id = NULL, *xin_id = NULL;
- u32 len;
- uint32_t setup_cnt = 0;
- int rc = 0, i;
struct mdss_data_type *mdata = platform_get_drvdata(pdev);
+ u32 offsets[MDSS_MDP_MAX_SSPP];
+ u32 ftch_id[MDSS_MDP_MAX_SSPP];
+ u32 xin_id[MDSS_MDP_MAX_SSPP];
+ u32 pnums[MDSS_MDP_MAX_SSPP];
+ struct mdss_mdp_pipe *pipe_list;
+ char prop_name[64];
+ int i, cnt, rc;
+
+ if (!out_plist)
+ return -EINVAL;
- offsets = kcalloc(npipes, sizeof(u32), GFP_KERNEL);
- if (!offsets)
- return -ENOMEM;
-
- ftch_id = kcalloc(npipes, sizeof(u32), GFP_KERNEL);
- if (!ftch_id) {
- rc = -ENOMEM;
- goto ftch_alloc_fail;
- }
-
- xin_id = kcalloc(npipes, sizeof(u32), GFP_KERNEL);
- if (!xin_id) {
- rc = -ENOMEM;
- goto xin_alloc_fail;
- }
-
- if (mdata->nvig_pipes) {
- mdata->vig_pipes = devm_kzalloc(&mdata->pdev->dev,
- (sizeof(struct mdss_mdp_pipe)
- * mdata->nvig_pipes), GFP_KERNEL);
- if (!mdata->vig_pipes) {
- rc = -ENOMEM;
- goto vig_alloc_fail;
- }
- if (nfids) {
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-vig-fetch-id", ftch_id,
- mdata->nvig_pipes);
- if (rc)
- goto parse_fail;
- }
-
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-vig-xin-id", xin_id,
- mdata->nvig_pipes);
- if (rc)
- goto parse_fail;
-
- rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-vig-off",
- offsets, mdata->nvig_pipes);
- if (rc)
- goto parse_fail;
-
- len = min_t(int, DEFAULT_TOTAL_VIG_PIPES,
- (int)mdata->nvig_pipes);
- rc = mdss_mdp_pipe_addr_setup(mdata, mdata->vig_pipes, offsets,
- ftch_id, xin_id, MDSS_MDP_PIPE_TYPE_VIG,
- MDSS_MDP_SSPP_VIG0, len, 0);
- if (rc)
- goto parse_fail;
-
- setup_cnt += len;
- }
-
- if (mdata->nrgb_pipes) {
- mdata->rgb_pipes = devm_kzalloc(&mdata->pdev->dev,
- (sizeof(struct mdss_mdp_pipe) *
- mdata->nrgb_pipes), GFP_KERNEL);
- if (!mdata->rgb_pipes) {
- rc = -ENOMEM;
- goto rgb_alloc_fail;
- }
-
- if (nfids) {
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-rgb-fetch-id",
- ftch_id + mdata->nvig_pipes,
- mdata->nrgb_pipes);
- if (rc)
- goto parse_fail;
+ for (i = 0, cnt = 0; i < MDSS_MDP_MAX_SSPP && cnt < len; i++) {
+ if (ptype == get_pipe_type_from_num(i)) {
+ pnums[cnt] = i;
+ cnt++;
}
-
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-rgb-xin-id",
- xin_id + mdata->nvig_pipes, mdata->nrgb_pipes);
- if (rc)
- goto parse_fail;
-
- rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-rgb-off",
- offsets + mdata->nvig_pipes, mdata->nrgb_pipes);
- if (rc)
- goto parse_fail;
-
- len = min_t(int, DEFAULT_TOTAL_RGB_PIPES,
- (int)mdata->nrgb_pipes);
- rc = mdss_mdp_pipe_addr_setup(mdata, mdata->rgb_pipes,
- offsets + mdata->nvig_pipes,
- ftch_id + mdata->nvig_pipes,
- xin_id + mdata->nvig_pipes,
- MDSS_MDP_PIPE_TYPE_RGB,
- MDSS_MDP_SSPP_RGB0, len, mdata->nvig_pipes);
- if (rc)
- goto parse_fail;
-
- setup_cnt += len;
}
- if (mdata->ndma_pipes) {
- mdata->dma_pipes = devm_kzalloc(&mdata->pdev->dev,
- sizeof(struct mdss_mdp_pipe) * mdata->ndma_pipes,
- GFP_KERNEL);
- if (!mdata->dma_pipes) {
- pr_err("no mem for dma_pipes: kzalloc fail\n");
- rc = -ENOMEM;
- goto dma_alloc_fail;
- }
+ if (cnt < len)
+ pr_warn("Invalid %s pipe count: %zu, max supported: %d\n",
+ ptypestr, len, cnt);
+ if (cnt == 0) {
+ *out_plist = NULL;
- dma_off = mdata->nvig_pipes + mdata->nrgb_pipes;
-
- if (nfids) {
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-dma-fetch-id",
- ftch_id + dma_off, mdata->ndma_pipes);
- if (rc)
- goto parse_fail;
- }
-
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-dma-xin-id",
- xin_id + dma_off, mdata->ndma_pipes);
- if (rc)
- goto parse_fail;
-
- rc = mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-dma-off",
- offsets + dma_off, mdata->ndma_pipes);
- if (rc)
- goto parse_fail;
-
- len = mdata->ndma_pipes;
- rc = mdss_mdp_pipe_addr_setup(mdata, mdata->dma_pipes,
- offsets + dma_off, ftch_id + dma_off, xin_id + dma_off,
- MDSS_MDP_PIPE_TYPE_DMA, MDSS_MDP_SSPP_DMA0, len,
- mdata->nvig_pipes + mdata->nrgb_pipes);
- if (rc)
- goto parse_fail;
-
- setup_cnt += len;
- }
-
- if (mdata->nvig_pipes > DEFAULT_TOTAL_VIG_PIPES) {
- rc = mdss_mdp_pipe_addr_setup(mdata,
- mdata->vig_pipes + DEFAULT_TOTAL_VIG_PIPES,
- offsets + DEFAULT_TOTAL_VIG_PIPES,
- ftch_id + DEFAULT_TOTAL_VIG_PIPES,
- xin_id + DEFAULT_TOTAL_VIG_PIPES,
- MDSS_MDP_PIPE_TYPE_VIG, setup_cnt,
- mdata->nvig_pipes - DEFAULT_TOTAL_VIG_PIPES,
- DEFAULT_TOTAL_VIG_PIPES);
- if (rc)
- goto parse_fail;
-
- setup_cnt += mdata->nvig_pipes - DEFAULT_TOTAL_VIG_PIPES;
- }
-
- if (mdata->nrgb_pipes > DEFAULT_TOTAL_RGB_PIPES) {
- rc = mdss_mdp_pipe_addr_setup(mdata,
- mdata->rgb_pipes + DEFAULT_TOTAL_RGB_PIPES,
- offsets + mdata->nvig_pipes + DEFAULT_TOTAL_RGB_PIPES,
- ftch_id + mdata->nvig_pipes + DEFAULT_TOTAL_RGB_PIPES,
- xin_id + mdata->nvig_pipes + DEFAULT_TOTAL_RGB_PIPES,
- MDSS_MDP_PIPE_TYPE_RGB, setup_cnt,
- mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES,
- mdata->nvig_pipes + DEFAULT_TOTAL_RGB_PIPES);
- if (rc)
- goto parse_fail;
-
- setup_cnt += mdata->nrgb_pipes - DEFAULT_TOTAL_RGB_PIPES;
- }
-
- if (mdata->nvig_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;
+ return 0;
}
- if (mdata->nrgb_pipes) {
- 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;
- }
+ pipe_list = devm_kzalloc(&pdev->dev,
+ (sizeof(struct mdss_mdp_pipe) * cnt), GFP_KERNEL);
+ if (!pipe_list)
+ return -ENOMEM;
- if (mdata->ndma_pipes) {
- rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
- "qcom,mdss-pipe-dma-clk-ctrl-offsets", mdata->dma_pipes,
- mdata->ndma_pipes);
+ if (mdata->has_pixel_ram || (ptype == MDSS_MDP_PIPE_TYPE_CURSOR)) {
+ for (i = 0; i < cnt; i++)
+ ftch_id[i] = -1;
+ } else {
+ snprintf(prop_name, sizeof(prop_name),
+ "qcom,mdss-pipe-%s-fetch-id", ptypestr);
+ rc = mdss_mdp_parse_dt_handler(pdev, prop_name, ftch_id,
+ cnt);
if (rc)
goto parse_fail;
}
- if (mdata->ncursor_pipes) {
- mdata->cursor_pipes = devm_kzalloc(&mdata->pdev->dev,
- sizeof(struct mdss_mdp_pipe) * mdata->ncursor_pipes,
- GFP_KERNEL);
+ snprintf(prop_name, sizeof(prop_name),
+ "qcom,mdss-pipe-%s-xin-id", ptypestr);
+ rc = mdss_mdp_parse_dt_handler(pdev, prop_name, xin_id, cnt);
+ if (rc)
+ goto parse_fail;
- if (!mdata->cursor_pipes) {
- pr_err("no mem for cursor_pipes: kzalloc fail\n");
- rc = -ENOMEM;
- goto cursor_alloc_fail;
- }
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-cursor-off", offsets,
- mdata->ncursor_pipes);
- if (rc)
- goto parse_fail;
+ snprintf(prop_name, sizeof(prop_name),
+ "qcom,mdss-pipe-%s-off", ptypestr);
+ rc = mdss_mdp_parse_dt_handler(pdev, prop_name, offsets, cnt);
+ if (rc)
+ goto parse_fail;
- rc = mdss_mdp_parse_dt_handler(pdev,
- "qcom,mdss-pipe-cursor-xin-id", xin_id,
- mdata->ncursor_pipes);
- if (rc)
- goto parse_fail;
+ rc = mdss_mdp_pipe_addr_setup(mdata, pipe_list, offsets, ftch_id,
+ xin_id, ptype, pnums, cnt, priority_base);
+ if (rc)
+ goto parse_fail;
- rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev,
- "qcom,mdss-pipe-cursor-clk-ctrl-offsets",
- mdata->cursor_pipes, mdata->ncursor_pipes);
- if (rc)
- goto parse_fail;
+ snprintf(prop_name, sizeof(prop_name),
+ "qcom,mdss-pipe-%s-clk-ctrl-offsets", ptypestr);
+ rc = mdss_mdp_parse_dt_pipe_clk_ctrl(pdev, prop_name,
+ pipe_list, cnt);
+ if (rc)
+ goto parse_fail;
- /* set the fetch id to an invalid value */
- for (i = 0; i < mdata->ncursor_pipes; i++)
- ftch_id[i] = -1;
- rc = mdss_mdp_pipe_addr_setup(mdata, mdata->cursor_pipes,
- offsets, ftch_id, xin_id, MDSS_MDP_PIPE_TYPE_CURSOR,
- MDSS_MDP_SSPP_CURSOR0, mdata->ncursor_pipes, 0);
- if (rc)
- goto parse_fail;
- pr_info("dedicated vp cursors detected, num=%d\n",
- mdata->ncursor_pipes);
- }
- goto parse_done;
+ *out_plist = pipe_list;
+ return cnt;
parse_fail:
- kfree(mdata->cursor_pipes);
-cursor_alloc_fail:
- kfree(mdata->cursor_pipes);
-dma_alloc_fail:
- kfree(mdata->rgb_pipes);
-rgb_alloc_fail:
- kfree(mdata->vig_pipes);
-vig_alloc_fail:
- kfree(xin_id);
-xin_alloc_fail:
- kfree(ftch_id);
-ftch_alloc_fail:
- kfree(offsets);
-parse_done:
+ devm_kfree(&pdev->dev, pipe_list);
+
return rc;
}
@@ -2826,9 +2766,34 @@ static int mdss_mdp_parse_dt_pipe(struct platform_device *pdev)
return -EINVAL;
}
- rc = mdss_mdp_parse_dt_pipe_helper(pdev, npipes, nfids);
- if (rc)
+ rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_VIG, "vig",
+ &mdata->vig_pipes, mdata->nvig_pipes, 0);
+ if (IS_ERR_VALUE(rc))
+ goto parse_fail;
+ mdata->nvig_pipes = rc;
+
+ rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_RGB, "rgb",
+ &mdata->rgb_pipes, mdata->nrgb_pipes,
+ mdata->nvig_pipes);
+ if (IS_ERR_VALUE(rc))
+ goto parse_fail;
+ mdata->nrgb_pipes = rc;
+
+ rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_DMA, "dma",
+ &mdata->dma_pipes, mdata->ndma_pipes,
+ mdata->nvig_pipes + mdata->nrgb_pipes);
+ if (IS_ERR_VALUE(rc))
goto parse_fail;
+ mdata->ndma_pipes = rc;
+
+ rc = mdss_mdp_parse_dt_pipe_helper(pdev, MDSS_MDP_PIPE_TYPE_CURSOR,
+ "cursor", &mdata->cursor_pipes, mdata->ncursor_pipes,
+ 0);
+ if (IS_ERR_VALUE(rc))
+ goto parse_fail;
+ mdata->ncursor_pipes = rc;
+
+ rc = 0;
mdss_mdp_parse_dt_handler(pdev, "qcom,mdss-pipe-sw-reset-off",
&sw_reset_offset, 1);
@@ -3820,9 +3785,11 @@ static int mdss_mdp_parse_dt_ppb_off(struct platform_device *pdev)
return -EINVAL;
}
+#ifdef CONFIG_MSM_BUS_SCALING
static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
{
int rc, paths;
+ struct device_node *node;
struct mdss_data_type *mdata = platform_get_drvdata(pdev);
rc = of_property_read_u32(pdev->dev.of_node,
@@ -3851,10 +3818,39 @@ static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
rc = -EINVAL;
pr_err("msm_bus_cl_get_pdata failed. rc=%d\n", rc);
mdata->bus_scale_table = NULL;
+ return rc;
+ }
+
+ /*
+ * if mdss-reg-bus is not found then default table is picked
+ * hence below code wont return error.
+ */
+ node = of_get_child_by_name(pdev->dev.of_node, "qcom,mdss-reg-bus");
+ if (node) {
+ mdata->reg_bus_scale_table =
+ msm_bus_pdata_from_node(pdev, node);
+ if (IS_ERR_OR_NULL(mdata->reg_bus_scale_table)) {
+ rc = PTR_ERR(mdata->reg_bus_scale_table);
+ if (!rc)
+ pr_err("bus_pdata reg_bus failed rc=%d\n", rc);
+ rc = 0;
+ mdata->reg_bus_scale_table = NULL;
+ }
+ } else {
+ rc = 0;
+ mdata->reg_bus_scale_table = NULL;
+ pr_debug("mdss-reg-bus not found\n");
}
return rc;
}
+#else
+static int mdss_mdp_parse_dt_bus_scale(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#endif
static int mdss_mdp_parse_dt_handler(struct platform_device *pdev,
char *prop_name, u32 *offsets, int len)
@@ -3878,7 +3874,7 @@ static int mdss_mdp_parse_dt_prop_len(struct platform_device *pdev,
of_find_property(pdev->dev.of_node, prop_name, &len);
if (len < 1) {
- pr_info("prop %s : doesn't exist in device tree\n",
+ pr_debug("prop %s : doesn't exist in device tree\n",
prop_name);
return 0;
}
@@ -4046,9 +4042,9 @@ static void apply_dynamic_ot_limit(u32 *ot_lim,
res = params->width * params->height;
- pr_debug("w:%d h:%d rot:%d yuv:%d wb:%d res:%d\n",
+ pr_debug("w:%d h:%d rot:%d yuv:%d wb:%d res:%d fps:%d\n",
params->width, params->height, params->is_rot,
- params->is_yuv, params->is_wb, res);
+ params->is_yuv, params->is_wb, res, params->frame_rate);
switch (mdata->mdp_rev) {
case MDSS_MDP_HW_REV_114:
@@ -4350,7 +4346,7 @@ int mdss_mdp_secure_display_ctrl(unsigned int enable)
&request, sizeof(request), &resp, sizeof(resp));
} else {
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
- MEM_PROTECT_SD_CTRL_FLAT), &desc);
+ mem_protect_sd_ctrl_id), &desc);
resp = desc.ret[0];
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 9505b18c5c45..6b36e2a9bc58 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -84,6 +84,8 @@
#define XIN_HALT_TIMEOUT_US 0x4000
+#define MAX_LAYER_COUNT 0xC
+
/* hw cursor can only be setup in highest mixer stage */
#define HW_CURSOR_STAGE(mdata) \
(((mdata)->max_target_zorder + MDSS_MDP_STAGE_0) - 1)
@@ -113,13 +115,73 @@ enum mdss_mdp_mixer_mux {
};
enum mdss_mdp_pipe_type {
- MDSS_MDP_PIPE_TYPE_UNUSED,
+ MDSS_MDP_PIPE_TYPE_INVALID,
MDSS_MDP_PIPE_TYPE_VIG,
MDSS_MDP_PIPE_TYPE_RGB,
MDSS_MDP_PIPE_TYPE_DMA,
MDSS_MDP_PIPE_TYPE_CURSOR,
};
+static inline enum mdss_mdp_sspp_index get_pipe_num_from_ndx(u32 ndx)
+{
+ u32 id;
+
+ if (unlikely(!ndx))
+ return MDSS_MDP_MAX_SSPP;
+
+ id = fls(ndx) - 1;
+
+ if (unlikely(ndx ^ BIT(id)))
+ return MDSS_MDP_MAX_SSPP;
+
+ return id;
+}
+
+static inline enum mdss_mdp_pipe_type
+get_pipe_type_from_num(enum mdss_mdp_sspp_index pnum)
+{
+ enum mdss_mdp_pipe_type ptype;
+
+ switch (pnum) {
+ case MDSS_MDP_SSPP_VIG0:
+ case MDSS_MDP_SSPP_VIG1:
+ case MDSS_MDP_SSPP_VIG2:
+ case MDSS_MDP_SSPP_VIG3:
+ ptype = MDSS_MDP_PIPE_TYPE_VIG;
+ break;
+ case MDSS_MDP_SSPP_RGB0:
+ case MDSS_MDP_SSPP_RGB1:
+ case MDSS_MDP_SSPP_RGB2:
+ case MDSS_MDP_SSPP_RGB3:
+ ptype = MDSS_MDP_PIPE_TYPE_RGB;
+ break;
+ case MDSS_MDP_SSPP_DMA0:
+ case MDSS_MDP_SSPP_DMA1:
+ case MDSS_MDP_SSPP_DMA2:
+ case MDSS_MDP_SSPP_DMA3:
+ ptype = MDSS_MDP_PIPE_TYPE_DMA;
+ break;
+ case MDSS_MDP_SSPP_CURSOR0:
+ case MDSS_MDP_SSPP_CURSOR1:
+ ptype = MDSS_MDP_PIPE_TYPE_CURSOR;
+ break;
+ default:
+ ptype = MDSS_MDP_PIPE_TYPE_INVALID;
+ break;
+ }
+
+ return ptype;
+}
+
+static inline enum mdss_mdp_pipe_type get_pipe_type_from_ndx(u32 ndx)
+{
+ enum mdss_mdp_sspp_index pnum;
+
+ pnum = get_pipe_num_from_ndx(ndx);
+
+ return get_pipe_type_from_num(pnum);
+}
+
enum mdss_mdp_block_type {
MDSS_MDP_BLOCK_UNUSED,
MDSS_MDP_BLOCK_SSPP,
@@ -190,6 +252,12 @@ struct mdss_mdp_vsync_handler {
struct list_head list;
};
+struct mdss_mdp_lineptr_handler {
+ bool enabled;
+ mdp_vsync_handler_t lineptr_handler;
+ struct list_head list;
+};
+
enum mdss_mdp_wb_ctl_type {
MDSS_MDP_WB_CTL_TYPE_BLOCK = 1,
MDSS_MDP_WB_CTL_TYPE_LINE
@@ -342,6 +410,8 @@ struct mdss_mdp_ctl {
struct work_struct recover_work;
struct work_struct remove_underrun_handler;
+ struct mdss_mdp_lineptr_handler lineptr_handler;
+
/*
* This ROI is aligned to as per following guidelines and
* sent to the panel driver.
@@ -550,6 +620,7 @@ struct pp_sts_type {
u32 gamut_sts;
u32 pgc_sts;
u32 sharp_sts;
+ u32 hist_sts;
u32 side_sts;
};
@@ -659,7 +730,9 @@ struct mdss_mdp_wfd;
struct mdss_overlay_private {
ktime_t vsync_time;
+ ktime_t lineptr_time;
struct kernfs_node *vsync_event_sd;
+ struct kernfs_node *lineptr_event_sd;
struct kernfs_node *hist_event_sd;
struct kernfs_node *bl_event_sd;
struct kernfs_node *ad_event_sd;
@@ -1186,6 +1259,60 @@ static inline int mdss_mdp_get_display_id(struct mdss_mdp_pipe *pipe)
return (pipe && pipe->mfd) ? pipe->mfd->index : -1;
}
+static inline bool mdss_mdp_is_full_frame_update(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_mdp_mixer *mixer;
+ struct mdss_rect *roi;
+
+ if (mdss_mdp_get_pu_type(ctl) != MDSS_MDP_DEFAULT_UPDATE)
+ return false;
+
+ if (ctl->mixer_left->valid_roi) {
+ mixer = ctl->mixer_left;
+ roi = &mixer->roi;
+ if ((roi->x != 0) || (roi->y != 0) || (roi->w != mixer->width)
+ || (roi->h != mixer->height))
+ return false;
+ }
+
+ if (ctl->mixer_right && ctl->mixer_right->valid_roi) {
+ mixer = ctl->mixer_right;
+ roi = &mixer->roi;
+ if ((roi->x != 0) || (roi->y != 0) || (roi->w != mixer->width)
+ || (roi->h != mixer->height))
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool mdss_mdp_is_lineptr_supported(struct mdss_mdp_ctl *ctl)
+{
+ struct mdss_panel_info *pinfo;
+
+ if (!ctl || !ctl->mixer_left || !ctl->is_master)
+ return false;
+
+ pinfo = &ctl->panel_data->panel_info;
+
+ return (((pinfo->type == MIPI_CMD_PANEL)
+ && (pinfo->te.tear_check_en)) ? true : false);
+}
+
+static inline bool mdss_mdp_is_map_needed(struct mdss_data_type *mdata,
+ struct mdss_mdp_img_data *data)
+{
+ u32 is_secure_ui = data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION;
+
+ /*
+ * For ULT Targets we need SMMU Map, to issue map call for secure Display.
+ */
+ if (is_secure_ui && !mdss_has_quirk(mdata, MDSS_QUIRK_NEED_SECURE_MAP))
+ return false;
+
+ return true;
+}
+
irqreturn_t mdss_mdp_isr(int irq, void *ptr);
void mdss_mdp_irq_clear(struct mdss_data_type *mdata,
u32 intr_type, u32 intf_num);
@@ -1412,8 +1539,8 @@ void mdss_mdp_smp_unreserve(struct mdss_mdp_pipe *pipe);
void mdss_mdp_smp_release(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
- struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_y_id, u32 *xin_id,
- u32 type, u32 num_base, u32 len, u8 priority_base);
+ struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_id, u32 *xin_id,
+ u32 type, const int *pnums, u32 len, u8 priority_base);
int mdss_mdp_mixer_addr_setup(struct mdss_data_type *mdata, u32 *mixer_offsets,
u32 *dspp_offsets, u32 *pingpong_offsets, u32 type, u32 len);
int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
@@ -1472,9 +1599,8 @@ int mdss_mdp_wb_kickoff(struct msm_fb_data_type *mfd,
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id);
-u32 mdss_mdp_get_mixer_mask(u32 pipe_num, u32 stage);
-u32 mdss_mdp_get_mixer_extn_mask(u32 pipe_num, u32 stage);
-u32 mdss_mdp_get_mixercfg(struct mdss_mdp_mixer *mixer, bool extn);
+bool mdss_mdp_mixer_reg_has_pipe(struct mdss_mdp_mixer *mixer,
+ struct mdss_mdp_pipe *pipe);
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);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 294d2248ec83..4fd5b3a1f7f7 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -27,7 +27,39 @@
#include "mdss_mdp_trace.h"
#include "mdss_debug.h"
+#define NUM_MIXERCFG_REGS 3
#define MDSS_MDP_WB_OUTPUT_BPP 3
+struct mdss_mdp_mixer_cfg {
+ u32 config_masks[NUM_MIXERCFG_REGS];
+ bool border_enabled;
+ bool cursor_enabled;
+};
+
+static struct {
+ u32 flush_bit;
+ struct mdss_mdp_hwio_cfg base;
+ struct mdss_mdp_hwio_cfg ext;
+ struct mdss_mdp_hwio_cfg ext2;
+} mdp_pipe_hwio[MDSS_MDP_MAX_SSPP] = {
+ [MDSS_MDP_SSPP_VIG0] = { 0, { 0, 3, 0 }, { 0, 1, 3 } },
+ [MDSS_MDP_SSPP_VIG1] = { 1, { 3, 3, 0 }, { 2, 1, 3 } },
+ [MDSS_MDP_SSPP_VIG2] = { 2, { 6, 3, 0 }, { 4, 1, 3 } },
+ [MDSS_MDP_SSPP_VIG3] = { 18, { 26, 3, 0 }, { 4, 1, 3 } },
+ [MDSS_MDP_SSPP_RGB0] = { 3, { 9, 3, 0 }, { 8, 1, 3 } },
+ [MDSS_MDP_SSPP_RGB1] = { 4, { 12, 3, 0 }, { 10, 1, 3 } },
+ [MDSS_MDP_SSPP_RGB2] = { 5, { 15, 3, 0 }, { 12, 1, 3 } },
+ [MDSS_MDP_SSPP_RGB3] = { 19, { 29, 3, 0 }, { 14, 1, 3 } },
+ [MDSS_MDP_SSPP_DMA0] = { 11, { 18, 3, 0 }, { 16, 1, 3 } },
+ [MDSS_MDP_SSPP_DMA1] = { 12, { 21, 3, 0 }, { 18, 1, 3 } },
+ [MDSS_MDP_SSPP_DMA2] = { 24, .ext2 = { 0, 4, 0 } },
+ [MDSS_MDP_SSPP_DMA3] = { 25, .ext2 = { 4, 4, 0 } },
+ [MDSS_MDP_SSPP_CURSOR0] = { 22, .ext = { 20, 4, 0 } },
+ [MDSS_MDP_SSPP_CURSOR1] = { 23, .ext = { 26, 4, 0 } },
+};
+
+static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
+ struct mdss_mdp_mixer_cfg *cfg);
+static void __mdss_mdp_reset_mixercfg(struct mdss_mdp_ctl *ctl);
static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
{
@@ -52,82 +84,6 @@ static DEFINE_MUTEX(mdss_mdp_ctl_lock);
static u32 mdss_mdp_get_vbp_factor_max(struct mdss_mdp_ctl *ctl);
-static inline u32 __mdss_mdp_get_wb_mixer(struct mdss_mdp_mixer *mixer)
-{
- /* Return the dedicated WB mixer. */
- if (test_bit(MDSS_CAPS_MIXER_1_FOR_WB,
- mixer->ctl->mdata->mdss_caps_map))
- return MDSS_MDP_INTF_LAYERMIXER1;
- else
- return MDSS_MDP_INTF_LAYERMIXER3;
-}
-
-static void __mdss_mdp_reset_mixercfg(struct mdss_mdp_ctl *ctl)
-{
- u32 off;
- int i, nmixers;
- struct mdss_data_type *mdata = mdss_mdp_get_mdata();
-
- if (!ctl || !mdata)
- return;
-
- nmixers = mdata->nmixers_intf + mdata->nmixers_wb;
-
- for (i = 0; i < nmixers; i++) {
- off = MDSS_MDP_REG_CTL_LAYER(i);
- mdss_mdp_ctl_write(ctl, off, 0);
-
- off += MDSS_MDP_REG_CTL_LAYER_EXTN(i);
- mdss_mdp_ctl_write(ctl, off, 0);
- }
-}
-
-static inline int __mdss_mdp_ctl_get_mixer_off(struct mdss_mdp_mixer *mixer)
-{
- u32 wb_mixer_num = 0;
-
- if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
- if (mixer->num == MDSS_MDP_INTF_LAYERMIXER3)
- return MDSS_MDP_CTL_X_LAYER_5;
- else
- return MDSS_MDP_REG_CTL_LAYER(mixer->num);
- } else {
- wb_mixer_num = __mdss_mdp_get_wb_mixer(mixer);
- return MDSS_MDP_REG_CTL_LAYER(mixer->num + wb_mixer_num);
- }
-}
-
-static inline int __mdss_mdp_ctl_get_mixer_extn_off(
- struct mdss_mdp_mixer *mixer)
-{
- u32 wb_mixer_num = 0;
-
- if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
- if (mixer->num == MDSS_MDP_INTF_LAYERMIXER3)
- return MDSS_MDP_REG_CTL_LAYER_EXTN(5);
- else
- return MDSS_MDP_REG_CTL_LAYER_EXTN(mixer->num);
- } else {
- wb_mixer_num = __mdss_mdp_get_wb_mixer(mixer);
- return MDSS_MDP_REG_CTL_LAYER_EXTN(wb_mixer_num);
- }
-}
-
-u32 mdss_mdp_get_mixercfg(struct mdss_mdp_mixer *mixer, bool extn)
-{
- u32 mixer_off;
-
- if (!mixer || !mixer->ctl)
- return 0;
-
- if (extn)
- mixer_off = __mdss_mdp_ctl_get_mixer_extn_off(mixer);
- else
- mixer_off = __mdss_mdp_ctl_get_mixer_off(mixer);
-
- return mdss_mdp_ctl_read(mixer->ctl, mixer_off);
-}
-
static inline u32 mdss_mdp_get_pclk_rate(struct mdss_mdp_ctl *ctl)
{
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
@@ -334,8 +290,8 @@ static u32 mdss_mdp_perf_calc_pipe_prefill_video(struct mdss_mdp_prefill_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 prefill_bytes = 0;
+ u32 latency_buf_bytes = 0;
u32 y_buf_bytes = 0;
u32 y_scaler_bytes = 0;
u32 pp_bytes = 0, pp_lines = 0;
@@ -958,7 +914,7 @@ static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
u32 prefill_val = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
bool apply_fudge = true;
- struct mdss_mdp_format_params *fmt;
+ struct mdss_mdp_format_params *fmt = NULL;
BUG_ON(num_pipes > MAX_PIPES_PER_LM);
@@ -1537,7 +1493,7 @@ int mdss_mdp_perf_bw_check_pipe(struct mdss_mdp_perf_params *perf,
{
struct mdss_data_type *mdata = pipe->mixer_left->ctl->mdata;
struct mdss_mdp_ctl *ctl = pipe->mixer_left->ctl;
- u32 vbp_fac, threshold;
+ u32 vbp_fac = 0, threshold = 0;
u64 prefill_bw, pipe_bw, max_pipe_bw;
/* we only need bandwidth check on real-time clients (interfaces) */
@@ -4027,9 +3983,11 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl,
(!l_roi->w && !l_roi->h && !r_roi->w && !r_roi->h) ||
!ctl->panel_data->panel_info.partial_update_enabled) {
- *l_roi = (struct mdss_rect) {0, 0,
- ctl->mixer_left->width,
- ctl->mixer_left->height};
+ if (ctl->mixer_left) {
+ *l_roi = (struct mdss_rect) {0, 0,
+ ctl->mixer_left->width,
+ ctl->mixer_left->height};
+ }
if (ctl->mixer_right) {
*r_roi = (struct mdss_rect) {0, 0,
@@ -4040,14 +3998,16 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl,
previous_frame_pu_type = mdss_mdp_get_pu_type(ctl);
mdss_mdp_set_mixer_roi(ctl->mixer_left, l_roi);
- ctl->roi = ctl->mixer_left->roi;
+ if (ctl->mixer_left)
+ ctl->roi = ctl->mixer_left->roi;
if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) {
struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
if (sctl) {
mdss_mdp_set_mixer_roi(sctl->mixer_left, r_roi);
- sctl->roi = sctl->mixer_left->roi;
+ if (sctl->mixer_left)
+ sctl->roi = sctl->mixer_left->roi;
}
} else if (is_dual_lm_single_display(ctl->mfd) && ctl->mixer_right) {
@@ -4057,7 +4017,7 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl,
ctl->roi.w += ctl->mixer_right->roi.w;
/* right_only, update roi.x as per CTL ROI guidelines */
- if (!ctl->mixer_left->valid_roi) {
+ if (ctl->mixer_left && !ctl->mixer_left->valid_roi) {
ctl->roi = ctl->mixer_right->roi;
ctl->roi.x = left_lm_w_from_mfd(ctl->mfd) +
ctl->mixer_right->roi.x;
@@ -4079,56 +4039,157 @@ void mdss_mdp_set_roi(struct mdss_mdp_ctl *ctl,
}
}
-u32 mdss_mdp_get_mixer_mask(u32 pipe_num, u32 stage)
+static void __mdss_mdp_mixer_update_cfg_masks(u32 pnum, u32 stage,
+ struct mdss_mdp_mixer_cfg *cfg)
{
- u32 mask = 0;
+ u32 masks[NUM_MIXERCFG_REGS] = { 0 };
+ int i;
- if ((pipe_num == MDSS_MDP_SSPP_VIG3 ||
- pipe_num == MDSS_MDP_SSPP_RGB3)) {
- /* Add 2 to account for Cursor & Border bits */
- mask = stage << ((3 * pipe_num) + 2);
- } else {
- mask = stage << (3 * pipe_num);
- }
- return mask;
+ if (pnum >= MDSS_MDP_MAX_SSPP)
+ return;
+
+ masks[0] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].base, stage);
+ masks[1] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].ext, stage);
+ masks[2] = mdss_mdp_hwio_mask(&mdp_pipe_hwio[pnum].ext2, stage);
+
+ for (i = 0; i < NUM_MIXERCFG_REGS; i++)
+ cfg->config_masks[i] |= masks[i];
+
+ pr_debug("pnum=%d stage=%d cfg=0x%08x ext=0x%08x\n",
+ pnum, stage, masks[0], masks[1]);
}
-u32 mdss_mdp_get_mixer_extn_mask(u32 pipe_num, u32 stage)
+static void __mdss_mdp_mixer_get_offsets(u32 mixer_num,
+ u32 *offsets, size_t count)
{
- u32 mask = 0;
+ BUG_ON(count < NUM_MIXERCFG_REGS);
+
+ offsets[0] = MDSS_MDP_REG_CTL_LAYER(mixer_num);
+ offsets[1] = MDSS_MDP_REG_CTL_LAYER_EXTN(mixer_num);
+ offsets[2] = MDSS_MDP_REG_CTL_LAYER_EXTN2(mixer_num);
+}
+static inline int __mdss_mdp_mixer_get_hw_num(struct mdss_mdp_mixer *mixer)
+{
/*
- * The ctl layer extension bits are ordered
- * VIG0-3, RGB0-3, DMA0-1
+ * mapping to hardware expectation of actual mixer programming to
+ * happen on following registers:
+ * INTF: 0, 1, 2, 5
+ * WB: 3, 4
+ * With some exceptions on certain revisions
*/
- if (pipe_num < MDSS_MDP_SSPP_RGB0) {
- mask = BIT(pipe_num << 1);
- } else if (pipe_num >= MDSS_MDP_SSPP_RGB0 &&
- pipe_num < MDSS_MDP_SSPP_DMA0) {
- mask = BIT((pipe_num + 1) << 1);
- } else if (pipe_num >= MDSS_MDP_SSPP_DMA0 &&
- pipe_num < MDSS_MDP_SSPP_VIG3) {
- mask = BIT((pipe_num + 2) << 1);
- } else if (pipe_num >= MDSS_MDP_SSPP_CURSOR0 &&
- pipe_num <= MDSS_MDP_SSPP_CURSOR1) {
- mask = stage << (20 + (6 * (pipe_num - MDSS_MDP_SSPP_CURSOR0)));
- } else if (pipe_num == MDSS_MDP_SSPP_VIG3) {
- mask = BIT(6);
- } else if (pipe_num == MDSS_MDP_SSPP_RGB3) {
- mask = BIT(14);
- }
-
- return mask;
+ if (mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) {
+ u32 wb_offset;
+
+ if (test_bit(MDSS_CAPS_MIXER_1_FOR_WB,
+ mixer->ctl->mdata->mdss_caps_map))
+ wb_offset = MDSS_MDP_INTF_LAYERMIXER1;
+ else
+ wb_offset = MDSS_MDP_INTF_LAYERMIXER3;
+
+ return mixer->num + wb_offset;
+ } else if (mixer->num == MDSS_MDP_INTF_LAYERMIXER3) {
+ return 5;
+ } else {
+ return mixer->num;
+ }
+}
+
+static inline void __mdss_mdp_mixer_write_layer(struct mdss_mdp_ctl *ctl,
+ u32 mixer_num, u32 *values, size_t count)
+{
+ u32 off[NUM_MIXERCFG_REGS];
+ int i;
+
+ BUG_ON(!values || count < NUM_MIXERCFG_REGS);
+
+ __mdss_mdp_mixer_get_offsets(mixer_num, off, ARRAY_SIZE(off));
+
+ for (i = 0; i < count; i++)
+ mdss_mdp_ctl_write(ctl, off[i], values[i]);
+}
+
+static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
+ struct mdss_mdp_mixer_cfg *cfg)
+{
+ u32 vals[NUM_MIXERCFG_REGS] = {0};
+ int i, mixer_num;
+
+ if (!mixer)
+ return;
+
+ mixer_num = __mdss_mdp_mixer_get_hw_num(mixer);
+
+ if (cfg) {
+ for (i = 0; i < NUM_MIXERCFG_REGS; i++)
+ vals[i] = cfg->config_masks[i];
+
+ if (cfg->border_enabled)
+ vals[0] |= MDSS_MDP_LM_BORDER_COLOR;
+ if (cfg->cursor_enabled)
+ vals[0] |= MDSS_MDP_LM_CURSOR_OUT;
+ }
+
+ __mdss_mdp_mixer_write_layer(mixer->ctl, mixer_num,
+ vals, ARRAY_SIZE(vals));
+
+ pr_debug("mixer=%d cfg=0%08x cfg_extn=0x%08x\n",
+ mixer->num, vals[0], vals[1]);
+ MDSS_XLOG(mixer->num, vals[0], vals[1]);
+}
+
+static void __mdss_mdp_reset_mixercfg(struct mdss_mdp_ctl *ctl)
+{
+ u32 vals[NUM_MIXERCFG_REGS] = {0};
+ int i, nmixers;
+
+ if (!ctl)
+ return;
+
+ nmixers = MDSS_MDP_INTF_MAX_LAYERMIXER + MDSS_MDP_WB_MAX_LAYERMIXER;
+
+ for (i = 0; i < nmixers; i++)
+ __mdss_mdp_mixer_write_layer(ctl, i, vals, ARRAY_SIZE(vals));
+}
+
+bool mdss_mdp_mixer_reg_has_pipe(struct mdss_mdp_mixer *mixer,
+ struct mdss_mdp_pipe *pipe)
+{
+ u32 offs[NUM_MIXERCFG_REGS];
+ u32 cfgs[NUM_MIXERCFG_REGS];
+ struct mdss_mdp_mixer_cfg mixercfg;
+ int i, mixer_num;
+
+ if (!mixer)
+ return false;
+
+ memset(&mixercfg, 0, sizeof(mixercfg));
+
+ mixer_num = __mdss_mdp_mixer_get_hw_num(mixer);
+ __mdss_mdp_mixer_get_offsets(mixer_num, offs, NUM_MIXERCFG_REGS);
+
+ for (i = 0; i < NUM_MIXERCFG_REGS; i++)
+ cfgs[i] = mdss_mdp_ctl_read(mixer->ctl, offs[i]);
+
+ __mdss_mdp_mixer_update_cfg_masks(pipe->num, -1, &mixercfg);
+ for (i = 0; i < NUM_MIXERCFG_REGS; i++) {
+ if (cfgs[i] & mixercfg.config_masks[i]) {
+ MDSS_XLOG(mixer->num, cfgs[0], cfgs[1]);
+ return true;
+ }
+ }
+
+ return false;
}
static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
int mixer_mux, bool lm_swap)
{
- int i;
+ int i, mixer_num;
int stage, screen_state, outsize;
u32 off, blend_op, blend_stage;
- u32 mixercfg = 0, mixer_op_mode = 0, bg_alpha_enable = 0,
- mixercfg_extn = 0;
+ u32 mixer_op_mode = 0, bg_alpha_enable = 0;
+ struct mdss_mdp_mixer_cfg mixercfg;
u32 fg_alpha = 0, bg_alpha = 0;
struct mdss_mdp_pipe *pipe;
struct mdss_mdp_ctl *ctl, *ctl_hw;
@@ -4149,10 +4210,12 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
/* check if mixer setup for rotator is needed */
if (mixer_hw->rotator_mode) {
- __mdss_mdp_reset_mixercfg(ctl_hw);
+ __mdss_mdp_mixer_write_cfg(mixer_hw, NULL);
return;
}
+ memset(&mixercfg, 0, sizeof(mixercfg));
+
if (lm_swap) {
if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT)
mixer = mdss_mdp_mixer_get(master_ctl,
@@ -4182,11 +4245,7 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
* mode is MDP_DUAL_LM_SINGLE_DISPLAY but update is only on
* one side.
*/
- off = __mdss_mdp_ctl_get_mixer_off(mixer_hw);
- mdss_mdp_ctl_write(ctl_hw, off, 0);
- /* Program ctl layer extension bits */
- off = __mdss_mdp_ctl_get_mixer_extn_off(mixer_hw);
- mdss_mdp_ctl_write(ctl_hw, off, 0);
+ __mdss_mdp_mixer_write_cfg(mixer_hw, NULL);
MDSS_XLOG(mixer->num, mixer_hw->num, XLOG_FUNC_EXIT);
return;
@@ -4200,19 +4259,16 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
mdp_mixer_write(mixer_hw, MDSS_MDP_REG_LM_OUT_SIZE, outsize);
if (screen_state == MDSS_SCREEN_FORCE_BLANK) {
- mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+ mixercfg.border_enabled = true;
goto update_mixer;
}
pipe = mixer->stage_pipe[MDSS_MDP_STAGE_BASE * MAX_PIPES_PER_STAGE];
if (pipe == NULL) {
- mixercfg = MDSS_MDP_LM_BORDER_COLOR;
+ mixercfg.border_enabled = true;
} else {
- if (pipe->type == MDSS_MDP_PIPE_TYPE_CURSOR)
- mixercfg_extn |= mdss_mdp_get_mixer_extn_mask(
- pipe->num, 1);
- else
- mixercfg |= mdss_mdp_get_mixer_mask(pipe->num, 1);
+ __mdss_mdp_mixer_update_cfg_masks(pipe->num,
+ MDSS_MDP_STAGE_BASE, &mixercfg);
if (pipe->src_fmt->alpha_enable)
bg_alpha_enable = 1;
@@ -4313,12 +4369,7 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
if (!pipe->src_fmt->alpha_enable && bg_alpha_enable)
mixer_op_mode = 0;
- if ((stage < MDSS_MDP_STAGE_6) &&
- (pipe->type != MDSS_MDP_PIPE_TYPE_CURSOR))
- mixercfg |= mdss_mdp_get_mixer_mask(pipe->num, stage);
- else
- mixercfg_extn |= mdss_mdp_get_mixer_extn_mask(
- pipe->num, stage);
+ __mdss_mdp_mixer_update_cfg_masks(pipe->num, stage, &mixercfg);
trace_mdp_sspp_change(pipe);
@@ -4333,20 +4384,11 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl,
}
if (mixer->cursor_enabled)
- mixercfg |= MDSS_MDP_LM_CURSOR_OUT;
+ mixercfg.cursor_enabled = true;
update_mixer:
- if (mixer_hw->num == MDSS_MDP_INTF_LAYERMIXER3) {
- ctl_hw->flush_bits |= BIT(20);
- } else if (mixer_hw->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) {
- if (test_bit(MDSS_CAPS_MIXER_1_FOR_WB,
- mdata->mdss_caps_map))
- ctl_hw->flush_bits |= BIT(7) << mixer_hw->num;
- else
- ctl_hw->flush_bits |= BIT(9) << mixer_hw->num;
- } else {
- ctl_hw->flush_bits |= BIT(6) << mixer_hw->num;
- }
+ mixer_num = __mdss_mdp_mixer_get_hw_num(mixer_hw);
+ ctl_hw->flush_bits |= BIT(mixer_num < 5 ? 6 + mixer_num : 20);
/* Read GC enable/disable status on LM */
mixer_op_mode |=
@@ -4362,18 +4404,14 @@ update_mixer:
mdp_mixer_write(mixer_hw, MDSS_MDP_REG_LM_BORDER_COLOR_1,
mdata->bcolor2 & 0xFFF);
- off = __mdss_mdp_ctl_get_mixer_off(mixer_hw);
- mdss_mdp_ctl_write(ctl_hw, off, mixercfg);
- /* Program ctl layer extension bits */
- off = __mdss_mdp_ctl_get_mixer_extn_off(mixer_hw);
- mdss_mdp_ctl_write(ctl_hw, off, mixercfg_extn);
+ __mdss_mdp_mixer_write_cfg(mixer_hw, &mixercfg);
- pr_debug("mixer=%d hw=%d cfg=0%08x cfg_extn=0x%08x op_mode=0x%08x w=%d h=%d bc0=0x%x bc1=0x%x\n",
- mixer->num, mixer_hw->num, mixercfg, mixercfg_extn,
+ pr_debug("mixer=%d hw=%d op_mode=0x%08x w=%d h=%d bc0=0x%x bc1=0x%x\n",
+ mixer->num, mixer_hw->num,
mixer_op_mode, mixer->roi.w, mixer->roi.h,
(mdata->bcolor0 & 0xFFF) | ((mdata->bcolor1 & 0xFFF) << 16),
mdata->bcolor2 & 0xFFF);
- MDSS_XLOG(mixer->num, mixer_hw->num, mixercfg, mixercfg_extn,
+ MDSS_XLOG(mixer->num, mixer_hw->num,
mixer_op_mode, mixer->roi.h, mixer->roi.w);
}
@@ -4567,19 +4605,10 @@ struct mdss_mdp_pipe *mdss_mdp_get_staged_pipe(struct mdss_mdp_ctl *ctl,
int mdss_mdp_get_pipe_flush_bits(struct mdss_mdp_pipe *pipe)
{
- u32 flush_bits;
-
- if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA)
- flush_bits |= BIT(pipe->num) << 5;
- else if (pipe->num == MDSS_MDP_SSPP_VIG3 ||
- pipe->num == MDSS_MDP_SSPP_RGB3)
- flush_bits |= BIT(pipe->num) << 10;
- else if (pipe->type == MDSS_MDP_PIPE_TYPE_CURSOR)
- flush_bits |= BIT(22 + pipe->num - MDSS_MDP_SSPP_CURSOR0);
- else /* RGB/VIG 0-2 pipes */
- flush_bits |= BIT(pipe->num);
+ if (WARN_ON(!pipe || pipe->num >= MDSS_MDP_MAX_SSPP))
+ return 0;
- return flush_bits;
+ return BIT(mdp_pipe_hwio[pipe->num].flush_bit);
}
int mdss_mdp_async_ctl_flush(struct msm_fb_data_type *mfd,
@@ -4630,17 +4659,12 @@ int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe,
j = i * MAX_PIPES_PER_STAGE;
/*
- * 1. If pipe is on the right side of the blending
- * stage, on either left LM or right LM but it is not
- * crossing LM boundry then right_blend ndx is used.
- * 2. If pipe is on the right side of the blending
- * stage on left LM and it is crossing LM boundry
- * then for left LM it is placed into right_blend
- * index but for right LM it still placed into
- * left_blend index.
+ * this could lead to cases where left blend index is
+ * not populated. For instance, where pipe is spanning
+ * across layer mixers. But this is handled properly
+ * within mixer programming code.
*/
- if (pipe->is_right_blend && (!pipe->src_split_req ||
- (pipe->src_split_req && !mixer->is_right_mixer)))
+ if (pipe->is_right_blend)
j++;
/* First clear all blend containers for current stage */
@@ -4694,27 +4718,44 @@ void mdss_mdp_mixer_unstage_all(struct mdss_mdp_mixer *mixer)
int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe,
struct mdss_mdp_mixer *mixer)
{
- int index;
- u8 right_blend_index;
+ int i, right_blend;
if (!pipe)
return -EINVAL;
if (!mixer)
return -EINVAL;
- right_blend_index = pipe->is_right_blend &&
- !(pipe->src_split_req && mixer->is_right_mixer);
- index = (pipe->mixer_stage * MAX_PIPES_PER_STAGE) + right_blend_index;
-
- if (index < MAX_PIPES_PER_LM && pipe == mixer->stage_pipe[index]) {
+ right_blend = pipe->is_right_blend ? 1 : 0;
+ i = (pipe->mixer_stage * MAX_PIPES_PER_STAGE) + right_blend;
+ if ((i < MAX_PIPES_PER_LM) && (pipe == mixer->stage_pipe[i])) {
pr_debug("unstage p%d from %s side of stage=%d lm=%d ndx=%d\n",
- pipe->num, pipe->is_right_blend ? "right" : "left",
- pipe->mixer_stage, mixer->num, index);
+ pipe->num, right_blend ? "right" : "left",
+ pipe->mixer_stage, mixer->num, i);
+ } else {
+ int stage;
- mixer->params_changed++;
- mixer->stage_pipe[index] = NULL;
+ for (i = 0; i < MAX_PIPES_PER_LM; i++) {
+ if (pipe != mixer->stage_pipe[i])
+ continue;
+
+ stage = i / MAX_PIPES_PER_STAGE;
+ right_blend = i & 1;
+
+ pr_warn("lm=%d pipe #%d stage=%d with %s blend, unstaged from %s side of stage=%d!\n",
+ mixer->num, pipe->num, pipe->mixer_stage,
+ pipe->is_right_blend ? "right" : "left",
+ right_blend ? "right" : "left", stage);
+ break;
+ }
+
+ /* pipe not found, not a failure */
+ if (i == MAX_PIPES_PER_LM)
+ return 0;
}
+ mixer->params_changed++;
+ mixer->stage_pipe[i] = NULL;
+
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 87d73e659c9c..9a0c1c9afa53 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -16,6 +16,23 @@
#include <linux/bitops.h>
+/*
+ * struct mdss_mdp_hwio_cfg - used to define a register bitfield
+ * @start: bitfield offset start from lsb
+ * @len: number of lsb bits that can be taken from field value
+ * @shift: number of lsb bits to truncate from field value
+ */
+struct mdss_mdp_hwio_cfg {
+ u32 start, len, shift;
+};
+
+static inline u32 mdss_mdp_hwio_mask(struct mdss_mdp_hwio_cfg *cfg, u32 val)
+{
+ u32 mask = (1 << cfg->len) - 1;
+
+ return ((val >> cfg->shift) & mask) << cfg->start;
+}
+
#define IGC_LUT_ENTRIES 256
#define GC_LUT_SEGMENTS 16
#define ENHIST_LUT_ENTRIES 256
@@ -141,16 +158,26 @@ enum mdss_mdp_ctl_index {
MDSS_MDP_MAX_CTL
};
+
+#define MDSS_MDP_REG_CTL_LAYER_EXTN_OFFSET 0x40
+#define MDSS_MDP_REG_CTL_LAYER_EXTN2_OFFSET 0x70
+#define MDSS_MDP_CTL_X_LAYER_5 0x24
+
+/* mixer 5 has different offset than others */
#define MDSS_MDP_REG_CTL_LAYER(lm) \
- ((lm == 5) ? (0x024) : ((lm) * 0x004))
+ (((lm) == 5) ? MDSS_MDP_CTL_X_LAYER_5 : ((lm) * 0x004))
+
#define MDSS_MDP_REG_CTL_LAYER_EXTN(lm) \
- ((lm == 5) ? (0x54) : (MDSS_MDP_REG_CTL_LAYER(lm) + 0x40))
+ (MDSS_MDP_REG_CTL_LAYER_EXTN_OFFSET + ((lm) * 0x004))
+
+#define MDSS_MDP_REG_CTL_LAYER_EXTN2(lm) \
+ (MDSS_MDP_REG_CTL_LAYER_EXTN2_OFFSET + ((lm) * 0x004))
+
#define MDSS_MDP_REG_CTL_TOP 0x014
#define MDSS_MDP_REG_CTL_FLUSH 0x018
#define MDSS_MDP_REG_CTL_START 0x01C
#define MDSS_MDP_REG_CTL_PACK_3D 0x020
#define MDSS_MDP_REG_CTL_SW_RESET 0x030
-#define MDSS_MDP_REG_CTL_LAYER_EXTN_OFFSET 0x40
#define MDSS_MDP_CTL_OP_VIDEO_MODE (0 << 17)
#define MDSS_MDP_CTL_OP_CMD_MODE (1 << 17)
@@ -180,7 +207,9 @@ enum mdss_mdp_sspp_index {
MDSS_MDP_SSPP_RGB3,
MDSS_MDP_SSPP_CURSOR0,
MDSS_MDP_SSPP_CURSOR1,
- MDSS_MDP_MAX_SSPP
+ MDSS_MDP_SSPP_DMA2,
+ MDSS_MDP_SSPP_DMA3,
+ MDSS_MDP_MAX_SSPP,
};
enum mdss_mdp_sspp_fetch_type {
@@ -361,10 +390,6 @@ enum mdss_mdp_sspp_chroma_samp_type {
#define MDSS_MDP_SCALEX_EN BIT(0)
#define MDSS_MDP_FMT_SOLID_FILL 0x4037FF
-#define MDSS_MDP_NUM_REG_MIXERS 3
-#define MDSS_MDP_NUM_WB_MIXERS 2
-#define MDSS_MDP_CTL_X_LAYER_5 0x24
-
#define MDSS_MDP_INTF_EDP_SEL (BIT(3) | BIT(1))
#define MDSS_MDP_INTF_HDMI_SEL (BIT(25) | BIT(24))
#define MDSS_MDP_INTF_DSI0_SEL BIT(8)
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index 950228e405a4..ae7b6a6ed015 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -13,6 +13,8 @@
#include <linux/kernel.h>
#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
#include "mdss_mdp.h"
#include "mdss_panel.h"
@@ -57,13 +59,17 @@ struct mdss_mdp_cmd_ctx {
u8 ref_cnt;
struct completion stop_comp;
+ atomic_t rdptr_cnt;
+ wait_queue_head_t rdptr_waitq;
struct completion pp_done;
wait_queue_head_t pp_waitq;
struct list_head vsync_handlers;
+ struct list_head lineptr_handlers;
int panel_power_state;
atomic_t koff_cnt;
u32 intf_stopped;
struct mutex mdp_rdptr_lock;
+ struct mutex mdp_wrptr_lock;
struct mutex clk_mtx;
spinlock_t clk_lock;
spinlock_t koff_lock;
@@ -86,8 +92,12 @@ struct mdss_mdp_cmd_ctx {
struct completion autorefresh_done;
int vsync_irq_cnt;
+ int lineptr_irq_cnt;
+ bool lineptr_enabled;
+ u32 prev_wr_ptr_irq;
struct mdss_intf_recovery intf_recovery;
+ struct mdss_intf_recovery intf_mdp_callback;
struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
u32 pp_timeout_report_cnt;
bool pingpong_split_slave;
@@ -101,6 +111,7 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx);
static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg);
static int mdss_mdp_disable_autorefresh(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *sctl);
+static int mdss_mdp_setup_vsync(struct mdss_mdp_cmd_ctx *ctx, bool enable);
static bool __mdss_mdp_cmd_is_aux_pp_needed(struct mdss_data_type *mdata,
struct mdss_mdp_ctl *mctl)
@@ -280,9 +291,10 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer,
cfg |= vclks_line;
- pr_debug("%s: yres=%d vclks=%x height=%d init=%d rd=%d start=%d\n",
+ pr_debug("%s: yres=%d vclks=%x height=%d init=%d rd=%d start=%d wr=%d\n",
__func__, pinfo->yres, vclks_line, te->sync_cfg_height,
- te->vsync_init_val, te->rd_ptr_irq, te->start_pos);
+ te->vsync_init_val, te->rd_ptr_irq, te->start_pos,
+ te->wr_ptr_irq);
pr_debug("thrd_start =%d thrd_cont=%d pp_split=%d\n",
te->sync_threshold_start, te->sync_threshold_continue,
ctx->pingpong_split_slave);
@@ -304,6 +316,9 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer,
MDSS_MDP_REG_PP_RD_PTR_IRQ,
te ? te->rd_ptr_irq : 0);
mdss_mdp_pingpong_write(pingpong_base,
+ MDSS_MDP_REG_PP_WR_PTR_IRQ,
+ te ? te->wr_ptr_irq : 0);
+ mdss_mdp_pingpong_write(pingpong_base,
MDSS_MDP_REG_PP_START_POS,
te ? te->start_pos : 0);
mdss_mdp_pingpong_write(pingpong_base,
@@ -649,7 +664,7 @@ int mdss_mdp_resource_control(struct mdss_mdp_ctl *ctl, u32 sw_event)
MDP_RSRC_CTL_STATE_OFF) {
/* Add an extra vote for the ahb bus */
mdss_update_reg_bus_vote(mdata->reg_bus_clt,
- VOTE_INDEX_19_MHZ);
+ VOTE_INDEX_LOW);
/* Enable MDP resources */
mdss_mdp_cmd_clk_on(ctx);
@@ -959,6 +974,17 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt));
complete_all(&ctx->rdptr_done);
+ /* If caller is waiting for the read pointer, notify. */
+ if (atomic_read(&ctx->rdptr_cnt)) {
+ if (atomic_add_unless(&ctx->rdptr_cnt, -1, 0)) {
+ MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
+ if (atomic_read(&ctx->rdptr_cnt))
+ pr_warn("%s: too many rdptrs=%d!\n",
+ __func__, atomic_read(&ctx->rdptr_cnt));
+ }
+ wake_up_all(&ctx->rdptr_waitq);
+ }
+
spin_lock(&ctx->clk_lock);
list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
if (tmp->enabled && !tmp->cmd_post_flush)
@@ -967,6 +993,104 @@ static void mdss_mdp_cmd_readptr_done(void *arg)
spin_unlock(&ctx->clk_lock);
}
+static int mdss_mdp_cmd_wait4readptr(struct mdss_mdp_cmd_ctx *ctx)
+{
+ int rc = 0;
+
+ rc = wait_event_timeout(ctx->rdptr_waitq,
+ atomic_read(&ctx->rdptr_cnt) == 0,
+ KOFF_TIMEOUT);
+ if (rc <= 0) {
+ if (atomic_read(&ctx->rdptr_cnt))
+ pr_err("timed out waiting for rdptr irq\n");
+ else
+ rc = 1;
+ }
+ return rc;
+}
+
+static void mdss_mdp_cmd_intf_callback(void *data, int event)
+{
+ struct mdss_mdp_cmd_ctx *ctx = data;
+ struct mdss_mdp_pp_tear_check *te = NULL;
+ u32 timeout_us = 3000, val = 0;
+ struct mdss_mdp_mixer *mixer;
+
+ if (!data) {
+ pr_err("%s: invalid ctx\n", __func__);
+ return;
+ }
+
+ if (!ctx->ctl)
+ return;
+
+ switch (event) {
+ case MDP_INTF_CALLBACK_DSI_WAIT:
+ pr_debug("%s: wait for frame cnt:%d event:%d\n",
+ __func__, atomic_read(&ctx->rdptr_cnt), event);
+
+ /*
+ * if we are going to suspended or pp split is not enabled,
+ * just return
+ */
+ if (ctx->intf_stopped || !is_pingpong_split(ctx->ctl->mfd))
+ return;
+ atomic_inc(&ctx->rdptr_cnt);
+
+ /* enable clks and rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, true);
+
+ mixer = mdss_mdp_mixer_get(ctx->ctl, MDSS_MDP_MIXER_MUX_LEFT);
+ if (!mixer) {
+ pr_err("%s: null mixer\n", __func__);
+ return;
+ }
+
+ /* wait for read pointer */
+ MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
+ pr_debug("%s: wait for frame cnt:%d\n",
+ __func__, atomic_read(&ctx->rdptr_cnt));
+ mdss_mdp_cmd_wait4readptr(ctx);
+
+ /* wait for 3ms to make sure we are within the frame */
+ te = &ctx->ctl->panel_data->panel_info.te;
+ readl_poll_timeout(mixer->pingpong_base +
+ MDSS_MDP_REG_PP_INT_COUNT_VAL, val,
+ (val & 0xffff) > (te->start_pos +
+ te->sync_threshold_start), 10, timeout_us);
+
+ /* disable rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, false);
+
+ break;
+ default:
+ pr_debug("%s: unhandled event=%d\n", __func__, event);
+ break;
+ }
+}
+
+static void mdss_mdp_cmd_writeptr_done(void *arg)
+{
+ struct mdss_mdp_ctl *ctl = arg;
+ struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
+ struct mdss_mdp_lineptr_handler *tmp;
+ ktime_t lineptr_time;
+
+ if (!ctx) {
+ pr_err("invalid ctx\n");
+ return;
+ }
+
+ lineptr_time = ktime_get();
+
+ spin_lock(&ctx->clk_lock);
+ list_for_each_entry(tmp, &ctx->lineptr_handlers, list) {
+ if (tmp->enabled)
+ tmp->lineptr_handler(ctl, lineptr_time);
+ }
+ spin_unlock(&ctx->clk_lock);
+}
+
static void mdss_mdp_cmd_intf_recovery(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
@@ -1020,6 +1144,7 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)
struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
struct mdss_mdp_vsync_handler *tmp;
ktime_t vsync_time;
+ bool sync_ppdone;
if (!ctx) {
pr_err("%s: invalid ctx\n", __func__);
@@ -1045,11 +1170,18 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)
MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->current_pp_num);
+ /*
+ * check state of sync ctx before decrementing koff_cnt to avoid race
+ * condition. That is, once both koff_cnt have been served and new koff
+ * can be triggered (sctx->koff_cnt could change)
+ */
+ sync_ppdone = mdss_mdp_cmd_do_notifier(ctx);
+
if (atomic_add_unless(&ctx->koff_cnt, -1, 0)) {
if (atomic_read(&ctx->koff_cnt))
pr_err("%s: too many kickoffs=%d!\n", __func__,
atomic_read(&ctx->koff_cnt));
- if (mdss_mdp_cmd_do_notifier(ctx)) {
+ if (sync_ppdone) {
atomic_inc(&ctx->pp_done_cnt);
schedule_work(&ctx->pp_done_work);
@@ -1071,6 +1203,178 @@ static void mdss_mdp_cmd_pingpong_done(void *arg)
spin_unlock(&ctx->koff_lock);
}
+static int mdss_mdp_setup_lineptr(struct mdss_mdp_cmd_ctx *ctx,
+ bool enable)
+{
+ int changed = 0;
+
+ mutex_lock(&ctx->mdp_wrptr_lock);
+
+ if (enable) {
+ if (ctx->lineptr_irq_cnt == 0)
+ changed++;
+ ctx->lineptr_irq_cnt++;
+ } else {
+ if (ctx->lineptr_irq_cnt) {
+ ctx->lineptr_irq_cnt--;
+ if (ctx->lineptr_irq_cnt == 0)
+ changed++;
+ } else {
+ pr_warn("%pS->%s: wr_ptr can not be turned off\n",
+ __builtin_return_address(0), __func__);
+ }
+ }
+
+ if (changed)
+ MDSS_XLOG(ctx->lineptr_irq_cnt, enable, current->pid);
+
+ pr_debug("%pS->%s: lineptr_irq_cnt=%d changed=%d enable=%d ctl:%d pp:%d\n",
+ __builtin_return_address(0), __func__,
+ ctx->lineptr_irq_cnt, changed, enable,
+ ctx->ctl->num, ctx->default_pp_num);
+
+ if (changed) {
+ if (enable) {
+ /* enable clocks and irq */
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_WR_PTR,
+ ctx->default_pp_num);
+ } else {
+ /* disable clocks and irq */
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_PING_PONG_WR_PTR,
+ ctx->default_pp_num);
+ /*
+ * check the intr status and clear the irq before
+ * disabling the clocks
+ */
+ mdss_mdp_intr_check_and_clear(
+ MDSS_MDP_IRQ_PING_PONG_WR_PTR,
+ ctx->default_pp_num);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
+ }
+ }
+
+ mutex_unlock(&ctx->mdp_wrptr_lock);
+ return ctx->lineptr_irq_cnt;
+}
+
+static int mdss_mdp_cmd_add_lineptr_handler(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_lineptr_handler *handle)
+{
+ struct mdss_mdp_cmd_ctx *ctx;
+ unsigned long flags;
+ int ret = 0;
+
+ mutex_lock(&ctl->offlock);
+ ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctl->is_master) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%pS->%s: ctl=%d\n",
+ __builtin_return_address(0), __func__, ctl->num);
+
+ MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt));
+
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (!handle->enabled) {
+ handle->enabled = true;
+ list_add(&handle->list, &ctx->lineptr_handlers);
+ }
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
+
+ if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY)
+ mutex_lock(&cmd_clk_mtx);
+
+ mdss_mdp_setup_lineptr(ctx, true);
+ ctx->lineptr_enabled = true;
+
+ if (ctl->mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY)
+ mutex_unlock(&cmd_clk_mtx);
+done:
+ mutex_unlock(&ctl->offlock);
+
+ return ret;
+}
+
+static int mdss_mdp_cmd_remove_lineptr_handler(struct mdss_mdp_ctl *ctl,
+ struct mdss_mdp_lineptr_handler *handle)
+{
+ struct mdss_mdp_cmd_ctx *ctx;
+ unsigned long flags;
+ bool disabled = true;
+
+ ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctl->is_master || !ctx->lineptr_enabled)
+ return -EINVAL;
+
+ pr_debug("%pS->%s: ctl=%d\n",
+ __builtin_return_address(0), __func__, ctl->num);
+
+ MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt));
+
+ spin_lock_irqsave(&ctx->clk_lock, flags);
+ if (handle->enabled) {
+ handle->enabled = false;
+ list_del_init(&handle->list);
+ } else {
+ disabled = false;
+ }
+ spin_unlock_irqrestore(&ctx->clk_lock, flags);
+
+ if (disabled)
+ mdss_mdp_setup_lineptr(ctx, false);
+ ctx->lineptr_enabled = false;
+ ctx->prev_wr_ptr_irq = 0;
+
+ return 0;
+}
+
+static int mdss_mdp_cmd_lineptr_ctrl(struct mdss_mdp_ctl *ctl, bool enable)
+{
+ struct mdss_mdp_pp_tear_check *te;
+ struct mdss_mdp_cmd_ctx *ctx;
+ int rc = 0;
+
+ ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX];
+ if (!ctx || !ctl->is_master)
+ return -EINVAL;
+
+ te = &ctl->panel_data->panel_info.te;
+ pr_debug("%pS->%s: ctl=%d en=%d, prev_lineptr=%d, lineptr=%d\n",
+ __builtin_return_address(0), __func__, ctl->num,
+ enable, ctx->prev_wr_ptr_irq, te->wr_ptr_irq);
+
+ if (enable) {
+ /* update reg only if the value has changed */
+ if (ctx->prev_wr_ptr_irq != te->wr_ptr_irq) {
+ ctx->prev_wr_ptr_irq = te->wr_ptr_irq;
+ mdss_mdp_pingpong_write(ctl->mixer_left->pingpong_base,
+ MDSS_MDP_REG_PP_WR_PTR_IRQ, te->wr_ptr_irq);
+ }
+
+ /*
+ * add handler only when lineptr is not enabled
+ * and wr ptr is non zero
+ */
+ if (!ctx->lineptr_enabled && te->wr_ptr_irq)
+ rc = mdss_mdp_cmd_add_lineptr_handler(ctl,
+ &ctl->lineptr_handler);
+ /* Disable handler when the value is zero */
+ else if (ctx->lineptr_enabled && !te->wr_ptr_irq)
+ rc = mdss_mdp_cmd_remove_lineptr_handler(ctl,
+ &ctl->lineptr_handler);
+ } else {
+ if (ctx->lineptr_enabled)
+ rc = mdss_mdp_cmd_remove_lineptr_handler(ctl,
+ &ctl->lineptr_handler);
+ }
+
+ return rc;
+}
+
/**
* mdss_mdp_cmd_autorefresh_pp_done() - pp done irq callback for autorefresh
* @arg: void pointer to the controller context.
@@ -1106,14 +1410,20 @@ static void pingpong_done_work(struct work_struct *work)
u32 status;
struct mdss_mdp_cmd_ctx *ctx =
container_of(work, typeof(*ctx), pp_done_work);
+ struct mdss_mdp_ctl *ctl = ctx->ctl;
- if (ctx->ctl) {
+ if (ctl) {
while (atomic_add_unless(&ctx->pp_done_cnt, -1, 0))
mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_DONE);
status = mdss_mdp_ctl_perf_get_transaction_status(ctx->ctl);
if (status == 0)
mdss_mdp_ctl_perf_release_bw(ctx->ctl);
+
+ if (!ctl->is_master)
+ ctl = mdss_mdp_get_main_ctl(ctl);
+ if (mdss_mdp_is_lineptr_supported(ctl))
+ mdss_mdp_cmd_lineptr_ctrl(ctl, false);
}
}
@@ -1557,7 +1867,7 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg)
mask = BIT(MDSS_MDP_IRQ_PING_PONG_COMP + ctx->current_pp_num);
status = mask & readl_relaxed(ctl->mdata->mdp_base +
MDSS_MDP_REG_INTR_STATUS);
- MDSS_XLOG(status, atomic_read(&ctx->koff_cnt), rc);
+ MDSS_XLOG(status, rc, atomic_read(&ctx->koff_cnt));
if (status) {
pr_warn("pp done but irq not triggered\n");
mdss_mdp_irq_clear(ctl->mdata,
@@ -1749,6 +2059,11 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl,
(void *)&ctx->intf_recovery,
CTL_INTF_EVENT_FLAG_DEFAULT);
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
+ (void *)&ctx->intf_mdp_callback,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
ctx->intf_stopped = 0;
if (sctx)
sctx->intf_stopped = 0;
@@ -2026,14 +2341,17 @@ static void mdss_mdp_cmd_autorefresh_done(void *arg)
static u32 get_autorefresh_timeout(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_cmd_ctx *ctx, u32 frame_cnt)
{
- struct mdss_mdp_mixer *mixer =
- mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
+ struct mdss_mdp_mixer *mixer;
struct mdss_panel_info *pinfo;
u32 line_count;
u32 fps, v_total;
unsigned long autorefresh_timeout;
pinfo = &ctl->panel_data->panel_info;
+ mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
+
+ if (!mixer || !pinfo)
+ return -EINVAL;
if (!ctx->ignore_external_te)
line_count = ctl->mixer_left->roi.h;
@@ -2362,6 +2680,13 @@ static int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg)
PERF_SW_COMMIT_STATE, PERF_STATUS_DONE);
}
+ if (mdss_mdp_is_lineptr_supported(ctl)) {
+ if (mdss_mdp_is_full_frame_update(ctl))
+ mdss_mdp_cmd_lineptr_ctrl(ctl, true);
+ else if (ctx->lineptr_enabled)
+ mdss_mdp_cmd_lineptr_ctrl(ctl, false);
+ }
+
/* Kickoff */
__mdss_mdp_kickoff(ctl, ctx);
@@ -2430,6 +2755,14 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
/* intf stopped, no more kickoff */
ctx->intf_stopped = 1;
+ /* Make sure any rd ptr for dsi callback is done before disable vsync */
+ if (is_pingpong_split(ctl->mfd)) {
+ pr_debug("%s will wait for rd ptr:%d\n", __func__,
+ atomic_read(&ctx->rdptr_cnt));
+ MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
+ mdss_mdp_cmd_wait4readptr(ctx);
+ }
+
/*
* if any vsyncs are still enabled, loop until the refcount
* goes to zero, so the rd ptr interrupt is disabled.
@@ -2442,11 +2775,20 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
while (mdss_mdp_setup_vsync(ctx, false))
;
}
+ if (ctx->lineptr_irq_cnt) {
+ WARN(1, "lineptr irq still enabled\n");
+ while (mdss_mdp_setup_lineptr(ctx, false))
+ ;
+ }
if (!ctl->pending_mode_switch) {
mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
}
/* shut down the MDP/DSI resources if still enabled */
@@ -2465,6 +2807,8 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
ctx->default_pp_num, NULL, NULL);
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_WR_PTR,
+ ctx->default_pp_num, NULL, NULL);
mdss_mdp_set_intr_callback_nosync(MDSS_MDP_IRQ_PING_PONG_COMP,
ctx->default_pp_num, NULL, NULL);
@@ -2521,6 +2865,8 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl,
list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list)
mdss_mdp_cmd_remove_vsync_handler(ctl, handle);
+ if (mdss_mdp_is_lineptr_supported(ctl))
+ mdss_mdp_cmd_lineptr_ctrl(ctl, false);
MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), XLOG_FUNC_ENTRY);
/* Command mode is supported only starting at INTF1 */
@@ -2531,7 +2877,7 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl,
int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
{
struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
- struct mdss_mdp_cmd_ctx *sctx;
+ struct mdss_mdp_cmd_ctx *sctx = NULL;
struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
bool panel_off = false;
bool turn_off_clocks = false;
@@ -2600,6 +2946,12 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
(void *)&ctx->intf_recovery,
CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
+ (void *)&ctx->intf_mdp_callback,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
ctx->intf_stopped = 0;
if (sctx)
sctx->intf_stopped = 0;
@@ -2754,6 +3106,7 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
ctx->pingpong_split_slave = pingpong_split_slave;
ctx->pp_timeout_report_cnt = 0;
init_waitqueue_head(&ctx->pp_waitq);
+ init_waitqueue_head(&ctx->rdptr_waitq);
init_completion(&ctx->stop_comp);
init_completion(&ctx->autorefresh_ppdone);
init_completion(&ctx->rdptr_done);
@@ -2763,6 +3116,7 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
spin_lock_init(&ctx->koff_lock);
mutex_init(&ctx->clk_mtx);
mutex_init(&ctx->mdp_rdptr_lock);
+ mutex_init(&ctx->mdp_wrptr_lock);
INIT_WORK(&ctx->gate_clk_work, clk_ctrl_gate_work);
INIT_DELAYED_WORK(&ctx->delayed_off_clk_work,
clk_ctrl_delayed_off_work);
@@ -2772,10 +3126,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
ctx->autorefresh_state = MDP_AUTOREFRESH_OFF;
ctx->autorefresh_frame_cnt = 0;
INIT_LIST_HEAD(&ctx->vsync_handlers);
+ INIT_LIST_HEAD(&ctx->lineptr_handlers);
ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery;
ctx->intf_recovery.data = ctx;
+ ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback;
+ ctx->intf_mdp_callback.data = ctx;
+
ctx->intf_stopped = 0;
pr_debug("%s: ctx=%p num=%d aux=%d\n", __func__, ctx,
@@ -2785,6 +3143,9 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR,
ctx->default_pp_num, mdss_mdp_cmd_readptr_done, ctl);
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_WR_PTR,
+ ctx->default_pp_num, mdss_mdp_cmd_writeptr_done, ctl);
+
ret = mdss_mdp_cmd_tearcheck_setup(ctx, false);
if (ret)
pr_err("tearcheck setup failed\n");
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index 4b7a76035586..e019bcf6eeaf 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -397,6 +397,7 @@ static int mdss_mdp_writeback_prepare_rot(struct mdss_mdp_ctl *ctl, void *arg)
struct mdss_mdp_writeback_arg *wb_args;
struct mdss_rot_entry *entry;
struct mdp_rotation_item *item;
+ struct mdss_rot_perf *perf;
struct mdss_data_type *mdata;
u32 format;
@@ -413,6 +414,7 @@ static int mdss_mdp_writeback_prepare_rot(struct mdss_mdp_ctl *ctl, void *arg)
return -ENODEV;
}
item = &entry->item;
+ perf = entry->perf;
mdata = ctl->mdata;
if (!mdata) {
pr_err("no mdata attached to ctl=%d", ctl->num);
@@ -433,6 +435,7 @@ static int mdss_mdp_writeback_prepare_rot(struct mdss_mdp_ctl *ctl, void *arg)
ctx->height = ctx->dst_rect.h = item->dst_rect.h;
ctx->dst_rect.x = item->dst_rect.x;
ctx->dst_rect.y = item->dst_rect.y;
+ ctx->frame_rate = perf->config.frame_rate;
ctx->dnsc_factor_w = entry->dnsc_factor_w;
ctx->dnsc_factor_h = entry->dnsc_factor_h;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index 06e106191f01..bc439a92524e 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, 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
@@ -36,16 +36,9 @@
#define CHECK_LAYER_BOUNDS(offset, size, max_size) \
(((size) > (max_size)) || ((offset) > ((max_size) - (size))))
-#define IS_PIPE_TYPE_CURSOR(pipe_ndx) \
- ((pipe_ndx >= (1 << MDSS_MDP_SSPP_CURSOR0)) &&\
- (pipe_ndx <= (1 << MDSS_MDP_SSPP_CURSOR1)))
-
-#define IS_PIPE_TYPE_DMA(pipe_ndx) \
- ((pipe_ndx >= (1 << MDSS_MDP_SSPP_DMA0)) &&\
- (pipe_ndx <= (1 << MDSS_MDP_SSPP_DMA1)))
-
#define SCALER_ENABLED \
(MDP_LAYER_ENABLE_PIXEL_EXT | MDP_LAYER_ENABLE_QSEED3_SCALE)
+
enum {
MDSS_MDP_RELEASE_FENCE = 0,
MDSS_MDP_RETIRE_FENCE,
@@ -57,25 +50,22 @@ enum layer_pipe_q {
LAYER_USES_DESTROY_PIPE_Q,
};
-static inline bool is_layer_right_blend(struct mdp_rect *left_blend,
- struct mdp_rect *right_blend, u32 left_lm_w)
-{
- return ((left_blend->x + left_blend->w) == right_blend->x) &&
- ((left_blend->x + left_blend->w) != left_lm_w) &&
- (left_blend->y == right_blend->y) &&
- (left_blend->h == right_blend->h);
-}
+enum layer_zorder_used {
+ LAYER_ZORDER_NONE = 0,
+ LAYER_ZORDER_LEFT = 1,
+ LAYER_ZORDER_RIGHT = 2,
+ LAYER_ZORDER_BOTH = 3,
+};
-static bool is_pipe_type_vig(struct mdss_data_type *mdata, u32 ndx)
+/*
+ * __layer_needs_src_split() - check needs source split configuration
+ * @layer: input layer
+ *
+ * return true if the layer should be used as source split
+ */
+static bool __layer_needs_src_split(struct mdp_input_layer *layer)
{
- u32 i;
-
- for (i = 0; i < mdata->nvig_pipes; i++) {
- if (mdata->vig_pipes[i].ndx == ndx)
- break;
- }
-
- return i < mdata->nvig_pipes;
+ return layer->flags & MDP_LAYER_ASYNC;
}
static int __async_update_position_check(struct msm_fb_data_type *mfd,
@@ -361,11 +351,16 @@ static int __validate_single_layer(struct msm_fb_data_type *mfd,
u32 bwc_enabled;
int ret;
bool is_vig_needed = false;
-
struct mdss_mdp_format_params *fmt;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
+ int ptype = get_pipe_type_from_ndx(layer->pipe_ndx);
+
+ if (ptype == MDSS_MDP_PIPE_TYPE_INVALID) {
+ pr_err("Invalid pipe ndx=%d\n", layer->pipe_ndx);
+ return -EINVAL;
+ }
if ((layer->dst_rect.w > mdata->max_mixer_width) ||
(layer->dst_rect.h > MAX_DST_H)) {
@@ -407,7 +402,7 @@ static int __validate_single_layer(struct msm_fb_data_type *mfd,
}
}
- if (IS_PIPE_TYPE_CURSOR(layer->pipe_ndx)) {
+ if (ptype == MDSS_MDP_PIPE_TYPE_CURSOR) {
ret = __cursor_layer_check(mfd, layer);
if (ret)
goto exit_fail;
@@ -435,14 +430,14 @@ static int __validate_single_layer(struct msm_fb_data_type *mfd,
(layer->src_rect.h != layer->dst_rect.h))))
is_vig_needed = true;
- if (is_vig_needed && !is_pipe_type_vig(mdata, layer->pipe_ndx)) {
+ if (is_vig_needed && ptype != MDSS_MDP_PIPE_TYPE_VIG) {
pr_err("pipe is non-scalar ndx=%x\n", layer->pipe_ndx);
ret = -EINVAL;
goto exit_fail;
}
- if ((IS_PIPE_TYPE_DMA(layer->pipe_ndx) ||
- IS_PIPE_TYPE_CURSOR(layer->pipe_ndx)) &&
+ if (((ptype == MDSS_MDP_PIPE_TYPE_DMA) ||
+ (ptype == MDSS_MDP_PIPE_TYPE_CURSOR)) &&
(layer->dst_rect.h != layer->src_rect.h ||
layer->dst_rect.w != layer->src_rect.w)) {
pr_err("no scaling supported on dma/cursor pipe, pipe num:%d\n",
@@ -551,6 +546,13 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
}
/*
+ * unstage the pipe if it's current z_order does not match with new
+ * z_order because client may only call the validate.
+ */
+ if (pipe->mixer_stage != layer->z_order)
+ mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
+
+ /*
* check if overlay span across two mixers and if source split is
* available. If yes, enable src_split_req flag so that during mixer
* staging, same pipe will be stagged on both layer mixers.
@@ -577,7 +579,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
pipe->is_right_blend = false;
}
- if (pipe->async_update && is_split_lm(mfd)) {
+ if (is_split_lm(mfd) && __layer_needs_src_split(layer)) {
pipe->src_split_req = true;
} else if ((mixer_mux == MDSS_MDP_MIXER_MUX_LEFT) &&
((layer->dst_rect.x + layer->dst_rect.w) > mixer->width)) {
@@ -1177,6 +1179,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
struct mdp_input_layer *layer, *prev_layer, *layer_list;
bool is_single_layer = false;
enum layer_pipe_q pipe_q_type;
+ enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
@@ -1201,6 +1204,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
}
for (i = 0; i < layer_count; i++) {
+ enum layer_zorder_used z = LAYER_ZORDER_NONE;
+
layer = &layer_list[i];
dst_x = layer->dst_rect.x;
left_blend_pipe = NULL;
@@ -1217,9 +1222,12 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
*
* Following logic of selecting left_blend has an inherent
* assumption that layer list is sorted on dst_x within a
- * same z_order.
+ * same z_order. Otherwise it will fail based on z_order checks.
*/
if (prev_layer && (prev_layer->z_order == layer->z_order)) {
+ struct mdp_rect *left = &prev_layer->dst_rect;
+ struct mdp_rect *right = &layer->dst_rect;
+
if ((layer->flags & MDP_LAYER_ASYNC)
|| (prev_layer->flags & MDP_LAYER_ASYNC)) {
ret = -EINVAL;
@@ -1228,13 +1236,46 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
goto validate_exit;
}
- if (is_layer_right_blend(&prev_layer->dst_rect,
- &layer->dst_rect, left_lm_w))
+ /*
+ * check if layer is right blend by checking it's
+ * directly to the right.
+ */
+ if (((left->x + left->w) == right->x) &&
+ (left->y == right->y) && (left->h == right->h))
left_blend_pipe = pipe;
+
+ /*
+ * if the layer is right at the left lm boundary and
+ * src split is not required then right blend is not
+ * required as it will lie only on the left mixer
+ */
+ if (!__layer_needs_src_split(prev_layer) &&
+ ((left->x + left->w) == left_lm_w))
+ left_blend_pipe = NULL;
+ }
+
+ if (__layer_needs_src_split(layer))
+ z = LAYER_ZORDER_BOTH;
+ else if (dst_x >= left_lm_w)
+ z = LAYER_ZORDER_RIGHT;
+ else if ((dst_x + layer->dst_rect.w) <= left_lm_w)
+ z = LAYER_ZORDER_LEFT;
+ else
+ z = LAYER_ZORDER_BOTH;
+
+ if (!left_blend_pipe && (layer->z_order >= MDSS_MDP_MAX_STAGE ||
+ (z & zorder_used[layer->z_order]))) {
+ pr_err("invalid z_order=%d or already in use %x\n",
+ layer->z_order, z);
+ ret = -EINVAL;
+ layer->error_code = ret;
+ goto validate_exit;
+ } else {
+ zorder_used[layer->z_order] |= z;
}
if ((layer->dst_rect.x < left_lm_w) ||
- (layer->flags & MDP_LAYER_ASYNC)) {
+ __layer_needs_src_split(layer)) {
is_single_layer = (left_lm_layers == 1);
mixer_mux = MDSS_MDP_MIXER_MUX_LEFT;
} else {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 00b12296b47e..57b6af00b8c1 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -1319,6 +1319,7 @@ 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;
+ struct mdss_data_type *mdata = mfd_to_mdata(mfd);
if (mdss_mdp_ctl_is_power_on(ctl)) {
if (!mdp5_data->mdata->batfet)
@@ -1334,6 +1335,10 @@ int mdss_mdp_overlay_start(struct msm_fb_data_type *mfd)
mfd->index);
return 0;
}
+ } else if (mdata->handoff_pending) {
+ pr_warn("fb%d: commit while splash handoff pending\n",
+ mfd->index);
+ return -EPERM;
}
pr_debug("starting fb%d overlay\n", mfd->index);
@@ -2619,6 +2624,30 @@ static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl,
sysfs_notify_dirent(mdp5_data->vsync_event_sd);
}
+/* function is called in irq context should have minimum processing */
+static void mdss_mdp_overlay_handle_lineptr(struct mdss_mdp_ctl *ctl,
+ ktime_t t)
+{
+ struct mdss_overlay_private *mdp5_data = NULL;
+
+ if (!ctl || !ctl->mfd) {
+ pr_warn("Invalid handle for lineptr\n");
+ return;
+ }
+
+ mdp5_data = mfd_to_mdp5_data(ctl->mfd);
+ if (!mdp5_data) {
+ pr_err("mdp5_data is NULL\n");
+ return;
+ }
+
+ pr_debug("lineptr irq on fb%d play_cnt=%d\n",
+ ctl->mfd->index, ctl->play_cnt);
+
+ mdp5_data->lineptr_time = t;
+ sysfs_notify_dirent(mdp5_data->lineptr_event_sd);
+}
+
int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -2891,6 +2920,78 @@ static ssize_t mdss_mdp_vsync_show_event(struct device *dev,
return ret;
}
+static ssize_t mdss_mdp_lineptr_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ u64 lineptr_ticks;
+ int ret;
+
+ if (!mdp5_data->ctl ||
+ (!mdp5_data->ctl->panel_data->panel_info.cont_splash_enabled
+ && !mdss_mdp_ctl_is_power_on(mdp5_data->ctl)))
+ return -EAGAIN;
+
+ lineptr_ticks = ktime_to_ns(mdp5_data->lineptr_time);
+
+ pr_debug("fb%d lineptr=%llu\n", mfd->index, lineptr_ticks);
+ ret = scnprintf(buf, PAGE_SIZE, "LINEPTR=%llu\n", lineptr_ticks);
+
+ return ret;
+}
+
+static ssize_t mdss_mdp_lineptr_show_value(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ int ret, lineptr_val;
+
+ if (!mdp5_data->ctl ||
+ (!mdp5_data->ctl->panel_data->panel_info.cont_splash_enabled
+ && !mdss_mdp_ctl_is_power_on(mdp5_data->ctl)))
+ return -EAGAIN;
+
+ lineptr_val = mfd->panel_info->te.wr_ptr_irq;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", lineptr_val);
+
+ return ret;
+}
+
+static ssize_t mdss_mdp_lineptr_set_value(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct msm_fb_data_type *mfd = fbi->par;
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ int ret, lineptr_value;
+
+ ret = kstrtoint(buf, 10, &lineptr_value);
+ if (ret) {
+ pr_err("Invalid input for ad\n");
+ return -EINVAL;
+ }
+
+ if (!mdp5_data->ctl ||
+ (!mdp5_data->ctl->panel_data->panel_info.cont_splash_enabled
+ && !mdss_mdp_ctl_is_power_on(mdp5_data->ctl)))
+ return -EAGAIN;
+
+ if (!mdss_mdp_is_lineptr_supported(mdp5_data->ctl)) {
+ pr_err("lineptr not supported\n");
+ return -ENOTSUPP;
+ }
+
+ /* the new lineptr value will take effect in the next kickoff */
+ mfd->panel_info->te.wr_ptr_irq = lineptr_value;
+
+ return count;
+}
+
static ssize_t mdss_mdp_bl_show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -3224,6 +3325,9 @@ static DEVICE_ATTR(msm_misr_en, S_IRUGO | S_IWUSR,
static DEVICE_ATTR(msm_cmd_autorefresh_en, S_IRUGO | S_IWUSR,
mdss_mdp_cmd_autorefresh_show, mdss_mdp_cmd_autorefresh_store);
static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL);
+static DEVICE_ATTR(lineptr_event, S_IRUGO, mdss_mdp_lineptr_show_event, NULL);
+static DEVICE_ATTR(lineptr_value, S_IRUGO | S_IWUSR | S_IWGRP,
+ mdss_mdp_lineptr_show_value, mdss_mdp_lineptr_set_value);
static DEVICE_ATTR(ad, S_IRUGO | S_IWUSR | S_IWGRP, mdss_mdp_ad_show,
mdss_mdp_ad_store);
static DEVICE_ATTR(dyn_pu, S_IRUGO | S_IWUSR | S_IWGRP, mdss_mdp_dyn_pu_show,
@@ -3235,6 +3339,8 @@ static DEVICE_ATTR(ad_bl_event, S_IRUGO, mdss_mdp_ad_bl_show_event, NULL);
static struct attribute *mdp_overlay_sysfs_attrs[] = {
&dev_attr_vsync_event.attr,
+ &dev_attr_lineptr_event.attr,
+ &dev_attr_lineptr_value.attr,
&dev_attr_ad.attr,
&dev_attr_dyn_pu.attr,
&dev_attr_msm_misr_en.attr,
@@ -4597,6 +4703,9 @@ static struct mdss_mdp_ctl *__mdss_mdp_overlay_ctl_init(
mdss_mdp_recover_underrun_handler;
ctl->recover_underrun_handler.cmd_post_flush = false;
+ ctl->lineptr_handler.lineptr_handler =
+ mdss_mdp_overlay_handle_lineptr;
+
INIT_WORK(&ctl->remove_underrun_handler,
remove_underrun_vsync_handler);
@@ -4699,7 +4808,8 @@ static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
goto panel_on;
if (!mfd->panel_info->cont_splash_enabled &&
- (mfd->panel_info->type != DTV_PANEL)) {
+ (mfd->panel_info->type != DTV_PANEL) &&
+ !mfd->panel_info->is_pluggable) {
rc = mdss_mdp_overlay_start(mfd);
if (rc)
goto end;
@@ -5360,6 +5470,14 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
goto init_fail;
}
+ mdp5_data->lineptr_event_sd = sysfs_get_dirent(dev->kobj.sd,
+ "lineptr_event");
+ if (!mdp5_data->lineptr_event_sd) {
+ pr_err("lineptr_event sysfs lookup failed\n");
+ rc = -ENODEV;
+ goto init_fail;
+ }
+
mdp5_data->hist_event_sd = sysfs_get_dirent(dev->kobj.sd,
"hist_event");
if (!mdp5_data->hist_event_sd) {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index dcf815f74d5d..44eded98e785 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -1394,46 +1394,33 @@ static void mdss_mdp_pipe_free(struct kref *kref)
static bool mdss_mdp_check_pipe_in_use(struct mdss_mdp_pipe *pipe)
{
int i;
- u32 mixercfg, mixercfg_extn, stage_off_mask, stage_off_extn_mask;
- u32 stage = BIT(0) | BIT(1) | BIT(2);
bool in_use = false;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_mixer *mixer;
- stage_off_mask = mdss_mdp_get_mixer_mask(pipe->num, stage);
- stage_off_extn_mask = mdss_mdp_get_mixer_extn_mask(pipe->num, stage);
-
for (i = 0; i < mdata->nctl; i++) {
ctl = mdata->ctl_off + i;
if (!ctl || !ctl->ref_cnt)
continue;
mixer = ctl->mixer_left;
- if (mixer && mixer->rotator_mode)
+ if (!mixer || mixer->rotator_mode)
continue;
- mixercfg = mdss_mdp_get_mixercfg(mixer, false);
- mixercfg_extn = mdss_mdp_get_mixercfg(mixer, true);
- if ((mixercfg & stage_off_mask) ||
- (mixercfg_extn & stage_off_extn_mask)) {
- pr_err("IN USE: mixer=%d pipe=%d mcfg:0x%x mask:0x%x mcfg_extn:0x%x mask_ext:0x%x\n",
- mixer->num, pipe->num,
- mixercfg, stage_off_mask,
- mixercfg_extn, stage_off_extn_mask);
+ if (mdss_mdp_mixer_reg_has_pipe(mixer, pipe)) {
+ in_use = true;
+ pr_err("IN USE: pipe=%d mixer=%d\n",
+ pipe->num, mixer->num);
MDSS_XLOG_TOUT_HANDLER("mdp", "vbif", "vbif_nrt",
"dbg_bus", "vbif_dbg_bus", "panic");
}
mixer = ctl->mixer_right;
- mixercfg = mdss_mdp_get_mixercfg(mixer, false);
- mixercfg_extn = mdss_mdp_get_mixercfg(mixer, true);
- if ((mixercfg & stage_off_mask) ||
- (mixercfg_extn & stage_off_extn_mask)) {
- pr_err("IN USE: mixer=%d pipe=%d mcfg:0x%x mask:0x%x mcfg_extn:0x%x mask_ext:0x%x\n",
- mixer->num, pipe->num,
- mixercfg, stage_off_mask,
- mixercfg_extn, stage_off_extn_mask);
+ if (mixer && mdss_mdp_mixer_reg_has_pipe(mixer, pipe)) {
+ in_use = true;
+ pr_err("IN USE: pipe=%d mixer=%d\n",
+ pipe->num, mixer->num);
MDSS_XLOG_TOUT_HANDLER("mdp", "vbif", "vbif_nrt",
"dbg_bus", "vbif_dbg_bus", "panic");
}
@@ -2035,7 +2022,7 @@ static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe)
int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
struct mdss_mdp_pipe *head, u32 *offsets, u32 *ftch_id, u32 *xin_id,
- u32 type, u32 num_base, u32 len, u8 priority_base)
+ u32 type, const int *pnums, u32 len, u8 priority_base)
{
u32 i;
@@ -2048,8 +2035,8 @@ int mdss_mdp_pipe_addr_setup(struct mdss_data_type *mdata,
head[i].type = type;
head[i].ftch_id = ftch_id[i];
head[i].xin_id = xin_id[i];
- head[i].num = i + num_base;
- head[i].ndx = BIT(i + num_base);
+ head[i].num = pnums[i];
+ head[i].ndx = BIT(pnums[i]);
head[i].priority = i + priority_base;
head[i].base = mdata->mdss_io.base + offsets[i];
pr_info("type:%d ftchid:%d xinid:%d num:%d ndx:0x%x prio:%d\n",
@@ -2152,6 +2139,7 @@ static void mdss_mdp_set_ot_limit_pipe(struct mdss_mdp_pipe *pipe)
ot_params.is_rot = pipe->mixer_left->rotator_mode;
ot_params.is_wb = ctl->intf_num == MDSS_MDP_NO_INTF;
ot_params.is_yuv = pipe->src_fmt->is_yuv;
+ ot_params.frame_rate = pipe->frame_rate;
/* rotator read uses nrt vbif */
if (mdss_mdp_is_nrt_vbif_base_defined(ctl->mdata) &&
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index f74e46dccdb9..6a4e31038d98 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -407,7 +407,8 @@ static struct mdss_pp_res_type *mdss_pp_res;
static u32 pp_hist_read(char __iomem *v_addr,
struct pp_hist_col_info *hist_info);
-static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix);
+static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
+ struct pp_sts_type *pp_sts);
static int pp_hist_disable(struct pp_hist_col_info *hist_info);
static void pp_update_pcc_regs(char __iomem *addr,
struct mdp_pcc_cfg_data *cfg_ptr);
@@ -506,6 +507,8 @@ static inline int pp_validate_dspp_mfd_block(struct msm_fb_data_type *mfd,
static int pp_mfd_release_all(struct msm_fb_data_type *mfd);
static int pp_mfd_ad_release_all(struct msm_fb_data_type *mfd);
static int mdss_mdp_ad_ipc_reset(struct msm_fb_data_type *mfd);
+static int pp_get_driver_ops(struct mdp_pp_driver_ops *ops);
+
static u32 last_sts, last_state;
static inline void mdss_mdp_pp_get_dcm_state(struct mdss_mdp_pipe *pipe,
@@ -529,6 +532,26 @@ inline int linear_map(int in, int *out, int in_max, int out_max)
}
+/**
+ * __get_hist_pipe() - get a pipe only if histogram is supported on it
+ * @pnum: pipe number desired
+ *
+ * returns the pipe with id only if the pipe supports sspp histogram
+ */
+static inline struct mdss_mdp_pipe *__get_hist_pipe(int pnum)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ enum mdss_mdp_pipe_type ptype;
+
+ ptype = get_pipe_type_from_num(pnum);
+
+ /* only VIG pipes support histogram */
+ if (ptype != MDSS_MDP_PIPE_TYPE_VIG)
+ return NULL;
+
+ return mdss_mdp_pipe_get(mdata, BIT(pnum));
+}
+
int mdss_mdp_csc_setup_data(u32 block, u32 blk_idx, struct mdp_csc_cfg *data)
{
int i, ret = 0;
@@ -1026,7 +1049,8 @@ static int pp_vig_pipe_setup(struct mdss_mdp_pipe *pipe, u32 *op)
}
/* Histogram collection enabled checked inside pp_hist_setup */
- pp_hist_setup(op, MDSS_PP_SSPP_CFG | pipe->num, pipe->mixer_left);
+ pp_hist_setup(op, MDSS_PP_SSPP_CFG | pipe->num, pipe->mixer_left,
+ &pipe->pp_res.pp_sts);
if (!(pipe->flags & MDP_OVERLAY_PP_CFG_EN)) {
pr_debug("Overlay PP CFG enable not set\n");
@@ -1797,6 +1821,7 @@ int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 current_opmode, location;
u32 dcm_state = DCM_UNINIT;
+ struct mdss_mdp_pipe *pipe_list;
if (pipe == NULL)
return -EINVAL;
@@ -1820,53 +1845,19 @@ int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
case MDSS_MDP_PIPE_TYPE_VIG:
pipe_base = mdata->mdp_base + MDSS_MDP_REG_IGC_VIG_BASE;
pipe_cnt = mdata->nvig_pipes;
+ pipe_list = mdata->vig_pipes;
location = SSPP_VIG;
- switch (pipe->num) {
- case MDSS_MDP_SSPP_VIG0:
- pipe_num = 0;
- break;
- case MDSS_MDP_SSPP_VIG1:
- pipe_num = 1;
- break;
- case MDSS_MDP_SSPP_VIG2:
- pipe_num = 2;
- break;
- case MDSS_MDP_SSPP_VIG3:
- pipe_num = 3;
- break;
- default:
- pr_err("Invalid pipe num %d pipe type %d\n",
- pipe->num, pipe->type);
- return -EINVAL;
- }
break;
case MDSS_MDP_PIPE_TYPE_RGB:
pipe_base = mdata->mdp_base + MDSS_MDP_REG_IGC_RGB_BASE;
pipe_cnt = mdata->nrgb_pipes;
+ pipe_list = mdata->rgb_pipes;
location = SSPP_RGB;
- switch (pipe->num) {
- case MDSS_MDP_SSPP_RGB0:
- pipe_num = 0;
- break;
- case MDSS_MDP_SSPP_RGB1:
- pipe_num = 1;
- break;
- case MDSS_MDP_SSPP_RGB2:
- pipe_num = 2;
- break;
- case MDSS_MDP_SSPP_RGB3:
- pipe_num = 3;
- break;
- default:
- pr_err("Invalid pipe num %d pipe type %d\n",
- pipe->num, pipe->type);
- return -EINVAL;
- }
break;
case MDSS_MDP_PIPE_TYPE_DMA:
pipe_base = mdata->mdp_base + MDSS_MDP_REG_IGC_DMA_BASE;
- pipe_num = pipe->num - MDSS_MDP_SSPP_DMA0;
pipe_cnt = mdata->ndma_pipes;
+ pipe_list = mdata->dma_pipes;
location = SSPP_DMA;
break;
case MDSS_MDP_PIPE_TYPE_CURSOR:
@@ -1877,6 +1868,17 @@ int mdss_mdp_pipe_sspp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
return -EINVAL;
}
+ for (pipe_num = 0; pipe_num < pipe_cnt; pipe_num++) {
+ if (pipe == (pipe_list + pipe_num))
+ break;
+ }
+
+ if (pipe_num == pipe_cnt) {
+ pr_err("Invalid pipe num %d pipe type %d\n",
+ pipe->num, pipe->type);
+ return -EINVAL;
+ }
+
if (pipe->pp_cfg.config_ops & MDP_OVERLAY_PP_IGC_CFG) {
flags |= PP_FLAGS_DIRTY_IGC;
if (!pp_ops[IGC].pp_set_config) {
@@ -2002,11 +2004,12 @@ static char __iomem *mdss_mdp_get_dspp_addr_off(u32 dspp_num)
}
/* Assumes that function will be called from within clock enabled space*/
-static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
+static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
+ struct pp_sts_type *pp_sts)
{
- int ret = -EINVAL;
+ int ret = 0;
char __iomem *base;
- u32 op_flags;
+ u32 op_flags = 0, block_type = 0;
struct mdss_mdp_pipe *pipe;
struct pp_hist_col_info *hist_info;
unsigned long flag;
@@ -2019,6 +2022,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
intr_mask = 1;
if (mix && (PP_LOCAT(block) == MDSS_PP_DSPP_CFG)) {
/* HIST_EN */
+ block_type = DSPP;
op_flags = BIT(16);
hist_info = &mdss_pp_res->dspp_hist[mix->num];
base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block));
@@ -2027,11 +2031,13 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
goto error;
}
} else if (PP_LOCAT(block) == MDSS_PP_SSPP_CFG &&
+ (pp_driver_ops.is_sspp_hist_supp) &&
(pp_driver_ops.is_sspp_hist_supp())) {
- pipe = mdss_mdp_pipe_get(mdata, BIT(PP_BLOCK(block)));
+ block_type = SSPP_VIG;
+ pipe = __get_hist_pipe(PP_BLOCK(block));
if (IS_ERR_OR_NULL(pipe)) {
pr_debug("pipe DNE (%d)\n",
- (u32) BIT(PP_BLOCK(block)));
+ (u32) PP_BLOCK(block));
ret = -ENODEV;
goto error;
}
@@ -2040,18 +2046,38 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
base = pipe->base;
mdss_mdp_pipe_unmap(pipe);
} else {
+ ret = -EINVAL;
goto error;
}
mutex_lock(&hist_info->hist_mutex);
spin_lock_irqsave(&hist_info->hist_lock, flag);
- if (hist_info->col_en) {
+ /*
+ * Set histogram interrupt if histogram collection is enabled. The
+ * interrupt register offsets are the same across different mdss
+ * versions so far, hence mdss_mdp_hist_irq_set_mask is used for
+ * all the mdss versions.
+ */
+ if (hist_info->col_en)
mdss_mdp_hist_irq_set_mask(intr_mask << hist_info->intr_shift);
+ /*
+ * Starting from msmcobalt, the histogram enable bit has been moved
+ * from DSPP opmode register to PA_HIST opmode register, hence we need
+ * to update the histogram enable bit differently based on mdss version.
+ * If HIST pp_set_config is defined, we will enable or disable the
+ * hist_en bit in PA_HIST opmode register inside HIST pp_set_config
+ * function; else, we only need to add the hist_en bit to the *op when
+ * histogram collection is enable, and *op will be passed to
+ * pp_dspp_setup to update the DSPP opmode register.
+ */
+ if (pp_ops[HIST].pp_set_config)
+ ret = pp_ops[HIST].pp_set_config(base, pp_sts, hist_info,
+ block_type);
+ else if (hist_info->col_en)
*op |= op_flags;
- }
+
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
mutex_unlock(&hist_info->hist_mutex);
- ret = 0;
error:
return ret;
}
@@ -2159,7 +2185,7 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
u32 ad_flags, flags, dspp_num, opmode = 0, ad_bypass;
struct mdp_pgc_lut_data *pgc_config;
struct pp_sts_type *pp_sts;
- char __iomem *base, *addr;
+ char __iomem *base, *addr = NULL;
int ret = 0;
struct mdss_data_type *mdata;
struct mdss_ad_info *ad = NULL;
@@ -2194,14 +2220,20 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
(pp_driver_ops.gamut_clk_gate_en))
pp_driver_ops.gamut_clk_gate_en(base +
mdata->pp_block_off.dspp_gamut_off);
- ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer);
- if (ret)
- goto dspp_exit;
- if (disp_num < MDSS_BLOCK_DISP_NUM)
+ if (disp_num < MDSS_BLOCK_DISP_NUM) {
+ pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
+ pp_sts->side_sts = side;
+
+ ret = pp_hist_setup(&opmode, MDSS_PP_DSPP_CFG | dspp_num, mixer,
+ pp_sts);
+ if (ret)
+ goto dspp_exit;
+
flags = mdss_pp_res->pp_disp_flags[disp_num];
- else
+ } else {
flags = 0;
+ }
mixer_cnt = mdss_mdp_get_ctl_mixers(disp_num, mixer_id);
if (dspp_num < mdata->nad_cfgs && disp_num < mdata->nad_cfgs &&
@@ -2217,9 +2249,6 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
if ((!flags) && (!(opmode)) && (!ad_flags))
goto dspp_exit;
- pp_sts = &mdss_pp_res->pp_disp_sts[disp_num];
- pp_sts->side_sts = side;
-
if (flags & PP_FLAGS_DIRTY_PA) {
if (!pp_ops[PA].pp_set_config) {
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) {
@@ -2471,7 +2500,7 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
IS_SIX_ZONE_DIRTY(flags, pa_v2_flags)));
if (mdata->pp_reg_bus_clt && max_bw_needed) {
ret = mdss_update_reg_bus_vote(mdata->pp_reg_bus_clt,
- VOTE_INDEX_80_MHZ);
+ VOTE_INDEX_HIGH);
if (ret)
pr_err("Updated reg_bus_scale failed, ret = %d", ret);
}
@@ -2769,8 +2798,7 @@ int mdss_mdp_pp_init(struct device *dev)
int i, ret = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
struct mdss_mdp_pipe *vig;
- struct pp_hist_col_info *hist;
- void *ret_ptr = NULL;
+ struct pp_hist_col_info *hist = NULL;
u32 ctl_off = 0;
if (!mdata)
@@ -2778,7 +2806,7 @@ int mdss_mdp_pp_init(struct device *dev)
mdata->pp_reg_bus_clt = mdss_reg_bus_vote_client_create("pp\0");
- if (IS_ERR_OR_NULL(mdata->pp_reg_bus_clt))
+ if (IS_ERR(mdata->pp_reg_bus_clt))
pr_err("bus client register failed\n");
mutex_lock(&mdss_pp_mutex);
@@ -2792,17 +2820,13 @@ int mdss_mdp_pp_init(struct device *dev)
if (mdss_mdp_pp_dt_parse(dev))
pr_info("No PP info in device tree\n");
- ret_ptr = pp_get_driver_ops(&pp_driver_ops);
- if (IS_ERR(ret_ptr)) {
+ ret = pp_get_driver_ops(&pp_driver_ops);
+ if (ret) {
pr_err("pp_get_driver_ops failed, ret=%d\n",
- (int) PTR_ERR(ret_ptr));
- ret = PTR_ERR(ret_ptr);
+ ret);
goto pp_exit;
- } else {
- mdss_pp_res->pp_data_res = ret_ptr;
- pp_ops = pp_driver_ops.pp_ops;
}
-
+ pp_ops = pp_driver_ops.pp_ops;
hist = devm_kzalloc(dev,
sizeof(struct pp_hist_col_info) *
mdata->ndspp,
@@ -2851,7 +2875,7 @@ int mdss_mdp_pp_init(struct device *dev)
vig[i].pp_res.hist.intr_shift = 10;
if (pp_driver_ops.get_hist_offset) {
ret = pp_driver_ops.get_hist_offset(
- DSPP, &ctl_off);
+ SSPP_VIG, &ctl_off);
if (ret) {
pr_err("get_hist_offset ret %d\n",
ret);
@@ -4646,16 +4670,9 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req)
for (i = 0; i < MDSS_PP_ARG_NUM; i++) {
if (!PP_ARG(i, req->block))
continue;
- pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ pipe = __get_hist_pipe(i);
if (IS_ERR_OR_NULL(pipe))
continue;
- if ((pipe->num > MDSS_MDP_SSPP_VIG2) &&
- (pipe->num != MDSS_MDP_SSPP_VIG3)) {
- ret = -EINVAL;
- pr_warn("Invalid Hist pipe (%d)\n", i);
- mdss_mdp_pipe_unmap(pipe);
- goto hist_stop_clk;
- }
hist_info = &pipe->pp_res.hist;
ret = pp_hist_enable(hist_info, req, NULL);
intr_mask = 1 << hist_info->intr_shift;
@@ -4780,16 +4797,10 @@ int mdss_mdp_hist_stop(u32 block)
for (i = 0; i < MDSS_PP_ARG_NUM; i++) {
if (!PP_ARG(i, block))
continue;
- pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ pipe = __get_hist_pipe(i);
if (IS_ERR_OR_NULL(pipe)) {
pr_warn("Invalid Hist pipe (%d)\n", i);
continue;
- } else if ((pipe->num > MDSS_MDP_SSPP_VIG2) &&
- (pipe->num != MDSS_MDP_SSPP_VIG3)) {
- mdss_mdp_pipe_unmap(pipe);
- pr_warn("Invalid Hist pipe (%d) pipe->num (%d)\n",
- i, pipe->num);
- continue;
}
hist_info = &pipe->pp_res.hist;
ret = pp_hist_disable(hist_info);
@@ -4820,7 +4831,7 @@ hist_stop_clk:
}
/**
- * mdss_mdp_hist_intr_req() - Request changes the histogram interupts
+ * mdss_mdp_hist_intr_req() - Request changes the histogram interrupts
* @intr: structure containting state of interrupt register
* @bits: the bits on interrupt register that should be changed
* @en: true if bits should be set, false if bits should be cleared
@@ -5142,7 +5153,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
}
}
- pipe = mdss_mdp_pipe_get(mdata, BIT(pipe_num));
+ pipe = __get_hist_pipe(pipe_num);
if (IS_ERR_OR_NULL(pipe)) {
pr_warn("Invalid starting hist pipe, %d\n", pipe_num);
ret = -ENODEV;
@@ -5154,15 +5165,10 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
if (!PP_ARG(i, hist->block))
continue;
pipe_cnt++;
- pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ pipe = __get_hist_pipe(i);
if (IS_ERR_OR_NULL(pipe)) {
pr_warn("Invalid Hist pipe (%d)\n", i);
continue;
- } else if ((pipe->num > MDSS_MDP_SSPP_VIG2) &&
- (pipe->num != MDSS_MDP_SSPP_VIG3)) {
- mdss_mdp_pipe_unmap(pipe);
- pr_warn("Invalid Hist pipe (%d)\n", i);
- continue;
}
hist_info = &pipe->pp_res.hist;
mdss_mdp_pipe_unmap(pipe);
@@ -5171,15 +5177,10 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
if (!PP_ARG(i, hist->block))
continue;
pipe_cnt++;
- pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ pipe = __get_hist_pipe(i);
if (IS_ERR_OR_NULL(pipe)) {
pr_warn("Invalid Hist pipe (%d)\n", i);
continue;
- } else if ((pipe->num > MDSS_MDP_SSPP_VIG2) &&
- (pipe->num != MDSS_MDP_SSPP_VIG3)) {
- mdss_mdp_pipe_unmap(pipe);
- pr_warn("Invalid Hist pipe (%d)\n", i);
- continue;
}
hist_info = &pipe->pp_res.hist;
ctl_base = pipe->base;
@@ -5197,15 +5198,10 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
if (!PP_ARG(i, hist->block))
continue;
pipe_cnt++;
- pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ pipe = __get_hist_pipe(i);
if (IS_ERR_OR_NULL(pipe)) {
pr_warn("Invalid Hist pipe (%d)\n", i);
continue;
- } else if ((pipe->num > MDSS_MDP_SSPP_VIG2) &&
- (pipe->num != MDSS_MDP_SSPP_VIG3)) {
- mdss_mdp_pipe_unmap(pipe);
- pr_warn("Invalid Hist pipe (%d)\n", i);
- continue;
}
hist_info = &pipe->pp_res.hist;
mdss_mdp_pipe_unmap(pipe);
@@ -5233,7 +5229,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) {
if (!PP_ARG(i, hist->block))
continue;
- pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ pipe = __get_hist_pipe(i);
if (IS_ERR_OR_NULL(pipe)) {
pr_warn("Invalid Hist pipe (%d)\n", i);
continue;
@@ -7304,3 +7300,43 @@ static inline int pp_validate_dspp_mfd_block(struct msm_fb_data_type *mfd,
return 0;
}
+
+static int pp_get_driver_ops(struct mdp_pp_driver_ops *ops)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ int ret = 0;
+ void *pp_cfg = NULL;
+
+ switch (mdata->mdp_rev) {
+ case MDSS_MDP_HW_REV_107:
+ case MDSS_MDP_HW_REV_107_1:
+ case MDSS_MDP_HW_REV_107_2:
+ case MDSS_MDP_HW_REV_114:
+ case MDSS_MDP_HW_REV_115:
+ case MDSS_MDP_HW_REV_116:
+ pp_cfg = pp_get_driver_ops_v1_7(ops);
+ if (IS_ERR_OR_NULL(pp_cfg))
+ ret = -EINVAL;
+ else
+ mdss_pp_res->pp_data_v1_7 = pp_cfg;
+ break;
+ case MDSS_MDP_HW_REV_300:
+ case MDSS_MDP_HW_REV_301:
+ pp_cfg = pp_get_driver_ops_v3(ops);
+ if (IS_ERR_OR_NULL(pp_cfg)) {
+ ret = -EINVAL;
+ } else {
+ mdss_pp_res->pp_data_v1_7 = pp_cfg;
+ /* Currently all caching data is used from v17 for V3
+ * hence setting the pointer to NULL. Will be used if we
+ * have to add any caching specific to V3.
+ */
+ mdss_pp_res->pp_data_v3 = NULL;
+ }
+ break;
+ default:
+ memset(ops, 0, sizeof(struct mdp_pp_driver_ops));
+ break;
+ }
+ return ret;
+}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.h b/drivers/video/fbdev/msm/mdss_mdp_pp.h
index acdebb7d43cf..31ddc7d49705 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.h
@@ -158,14 +158,16 @@ struct mdss_pp_res_type {
/* physical info */
struct pp_hist_col_info *dspp_hist;
/*
- * The pp_data_res will be a pointer to newer MDP revisions of the
+ * The pp_data_v1_7 will be a pointer to newer MDP revisions of the
* pp_res, which will hold the cfg_payloads of each feature in a single
* struct.
*/
- void *pp_data_res;
+ void *pp_data_v1_7;
+ void *pp_data_v3;
};
-void *pp_get_driver_ops(struct mdp_pp_driver_ops *ops);
+void *pp_get_driver_ops_v1_7(struct mdp_pp_driver_ops *ops);
+void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops);
static inline void pp_sts_set_split_bits(u32 *sts, u32 bits)
{
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
index 7769a8fbf644..d1b3f1a89812 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_cache_config.c
@@ -112,12 +112,12 @@ static int pp_hist_lut_cache_params_v1_7(struct mdp_hist_lut_data *config,
pr_err("invalid config block %d\n", config->block);
return -EINVAL;
}
- if (!mdss_pp_res->pp_data_res) {
- pr_err("invalid pp_data_res %p\n", mdss_pp_res->pp_data_res);
+ if (!mdss_pp_res->pp_data_v1_7) {
+ pr_err("invalid pp_data_v1_7 %p\n", mdss_pp_res->pp_data_v1_7);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if (config->ops & MDP_PP_OPS_READ) {
pr_err("read op is not supported\n");
return -EINVAL;
@@ -295,12 +295,12 @@ int pp_dither_cache_params_v1_7(struct mdp_dither_cfg_data *config,
pr_err("invalid config block %d\n", config->block);
return -EINVAL;
}
- if (!mdss_pp_res->pp_data_res) {
- pr_err("invalid pp_data_res %p\n", mdss_pp_res->pp_data_res);
+ if (!mdss_pp_res->pp_data_v1_7) {
+ pr_err("invalid pp_data_v1_7 %p\n", mdss_pp_res->pp_data_v1_7);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
pr_warn("Can't set both split bits\n");
@@ -397,11 +397,11 @@ static int pp_gamut_cache_params_v1_7(struct mdp_gamut_cfg_data *config,
pr_err("invalid config block %d\n", config->block);
return -EINVAL;
}
- if (!mdss_pp_res->pp_data_res) {
- pr_err("invalid pp_data_res %p\n", mdss_pp_res->pp_data_res);
+ if (!mdss_pp_res->pp_data_v1_7) {
+ pr_err("invalid pp_data_v1_7 %p\n", mdss_pp_res->pp_data_v1_7);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if (config->flags & MDP_PP_OPS_READ) {
pr_err("read op is not supported\n");
return -EINVAL;
@@ -646,12 +646,12 @@ static int pp_pcc_cache_params_v1_7(struct mdp_pcc_cfg_data *config,
pr_err("invalid config block %d\n", config->block);
return -EINVAL;
}
- if (!mdss_pp_res->pp_data_res) {
- pr_err("invalid pp_data_res %p\n", mdss_pp_res->pp_data_res);
+ if (!mdss_pp_res->pp_data_v1_7) {
+ pr_err("invalid pp_data_v1_7 %p\n", mdss_pp_res->pp_data_v1_7);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if (config->ops & MDP_PP_OPS_READ) {
pr_err("read op is not supported\n");
return -EINVAL;
@@ -744,11 +744,11 @@ static int pp_igc_lut_cache_params_v1_7(struct mdp_igc_lut_data *config,
pr_err("invalid config block %d\n", config->block);
return -EINVAL;
}
- if (!mdss_pp_res->pp_data_res) {
- pr_err("invalid pp_data_res %p\n", mdss_pp_res->pp_data_res);
+ if (!mdss_pp_res->pp_data_v1_7) {
+ pr_err("invalid pp_data_v1_7 %p\n", mdss_pp_res->pp_data_v1_7);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if (config->ops & MDP_PP_OPS_READ) {
pr_err("read op is not supported\n");
return -EINVAL;
@@ -1019,7 +1019,7 @@ static int pp_pgc_lut_cache_params_v1_7(struct mdp_pgc_lut_data *config,
pr_err("invalid disp_num %d\n", disp_num);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if (!res_cache) {
pr_err("invalid resource payload\n");
return -EINVAL;
@@ -1139,12 +1139,12 @@ static int pp_pa_cache_params_v1_7(struct mdp_pa_v2_cfg_data *config,
return -EINVAL;
}
- if (!mdss_pp_res->pp_data_res) {
- pr_err("Invalid pp_data_res %p\n", mdss_pp_res->pp_data_res);
+ if (!mdss_pp_res->pp_data_v1_7) {
+ pr_err("Invalid pp_data_v1_7 %p\n", mdss_pp_res->pp_data_v1_7);
return -EINVAL;
}
- res_cache = mdss_pp_res->pp_data_res;
+ res_cache = mdss_pp_res->pp_data_v1_7;
if (config->flags & MDP_PP_OPS_READ) {
pr_err("Read op is not supported\n");
return -EINVAL;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_common.c b/drivers/video/fbdev/msm/mdss_mdp_pp_common.c
new file mode 100644
index 000000000000..7742b5e4ad0c
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_common.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+#include "mdss_mdp_pp_common.h"
+
+void pp_pa_set_sts(struct pp_sts_type *pp_sts,
+ struct mdp_pa_data_v1_7 *pa_data,
+ int enable_flag, int block_type)
+{
+ if (!pp_sts) {
+ pr_err("invalid input pp_sts %p\n", pp_sts);
+ return;
+ }
+
+ pp_sts->pa_sts = 0;
+
+ if (enable_flag & MDP_PP_OPS_DISABLE) {
+ pp_sts->pa_sts &= ~PP_STS_ENABLE;
+ return;
+ } else if (enable_flag & MDP_PP_OPS_ENABLE) {
+ pp_sts->pa_sts |= PP_STS_ENABLE;
+ }
+
+ if (!pa_data) {
+ pr_err("invalid input pa_data %p\n", pa_data);
+ return;
+ }
+
+ /* Global HSV STS update */
+ if (pa_data->mode & MDP_PP_PA_HUE_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_HUE_MASK;
+ if (pa_data->mode & MDP_PP_PA_SAT_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_SAT_MASK;
+ if (pa_data->mode & MDP_PP_PA_VAL_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_VAL_MASK;
+ if (pa_data->mode & MDP_PP_PA_CONT_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_CONT_MASK;
+ if (pa_data->mode & MDP_PP_PA_SAT_ZERO_EXP_EN)
+ pp_sts->pa_sts |= PP_STS_PA_SAT_ZERO_EXP_EN;
+
+ /* Memory Protect STS update */
+ if (pa_data->mode & MDP_PP_PA_MEM_PROT_HUE_EN)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_HUE_EN;
+ if (pa_data->mode & MDP_PP_PA_MEM_PROT_SAT_EN)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_SAT_EN;
+ if (pa_data->mode & MDP_PP_PA_MEM_PROT_VAL_EN)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_VAL_EN;
+ if (pa_data->mode & MDP_PP_PA_MEM_PROT_CONT_EN)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_CONT_EN;
+ if (pa_data->mode & MDP_PP_PA_MEM_PROT_BLEND_EN)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_BLEND_EN;
+ if ((block_type == DSPP) &&
+ (pa_data->mode & MDP_PP_PA_MEM_PROT_SIX_EN))
+ pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_SIX_EN;
+
+ /* Memory Color STS update */
+ if (pa_data->mode & MDP_PP_PA_MEM_COL_SKIN_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_COL_SKIN_MASK;
+ if (pa_data->mode & MDP_PP_PA_MEM_COL_SKY_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_COL_SKY_MASK;
+ if (pa_data->mode & MDP_PP_PA_MEM_COL_FOL_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_MEM_COL_FOL_MASK;
+
+ /* Six Zone STS update */
+ if (block_type == DSPP) {
+ if (pa_data->mode & MDP_PP_PA_SIX_ZONE_HUE_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_HUE_MASK;
+ if (pa_data->mode & MDP_PP_PA_SIX_ZONE_SAT_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_SAT_MASK;
+ if (pa_data->mode & MDP_PP_PA_SIX_ZONE_VAL_MASK)
+ pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_VAL_MASK;
+
+ pp_sts_set_split_bits(&pp_sts->pa_sts, enable_flag);
+ }
+}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_common.h b/drivers/video/fbdev/msm/mdss_mdp_pp_common.h
new file mode 100644
index 000000000000..b835f8fc1621
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_common.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef MDSS_MDP_PP_COMMON_H
+#define MDSS_MDP_PP_COMMON_H
+
+#include "mdss_mdp.h"
+#include "mdss_mdp_pp.h"
+
+#define JUMP_REGISTERS_OFF(n) ((n) * (sizeof(uint32_t)))
+#define REG_MASK(n) ((BIT(n)) - 1)
+#define REG_MASK_SHIFT(n, shift) ((REG_MASK(n)) << (shift))
+
+void pp_pa_set_sts(struct pp_sts_type *pp_sts,
+ struct mdp_pa_data_v1_7 *pa_data,
+ int enable_flag, int block_type);
+#endif
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c
index b6b2d2c3c0f0..86312366571d 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v1_7.c
@@ -18,6 +18,7 @@
#include "mdss_fb.h"
#include "mdss_mdp.h"
#include "mdss_mdp_pp.h"
+#include "mdss_mdp_pp_common.h"
/* MDP v1.7 specific macros */
@@ -242,7 +243,7 @@ static int pp_dither_get_version(u32 *version);
static int pp_hist_lut_get_version(u32 *version);
static void pp_gamut_clock_gating_en(char __iomem *base_addr);
-void *pp_get_driver_ops(struct mdp_pp_driver_ops *ops)
+void *pp_get_driver_ops_v1_7(struct mdp_pp_driver_ops *ops)
{
if (!ops) {
pr_err("PP driver ops invalid %p\n", ops);
@@ -420,7 +421,7 @@ static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data,
hist_addr += 4;
}
if (copy_to_user(lut_data->data, data, sz)) {
- pr_err("faild to copy the hist_lut back to user\n");
+ pr_err("failed to copy the hist_lut back to user\n");
ret = -EFAULT;
}
kfree(data);
@@ -502,7 +503,8 @@ static int pp_hist_lut_set_config(char __iomem *base_addr,
}
if (lut_cfg_data->hist_lut_first)
pp_sts->enhist_sts |= PP_STS_PA_LUT_FIRST;
-
+ else
+ pp_sts->enhist_sts &= ~PP_STS_PA_LUT_FIRST;
writel_relaxed(1, swap_addr);
@@ -1269,74 +1271,6 @@ static void pp_pa_set_six_zone(char __iomem *base_addr,
*pa_hold_mask |= PA_HOLD_SIX_ZONE_MASK;
}
-static void pp_pa_set_sts(struct pp_sts_type *pp_sts,
- struct mdp_pa_data_v1_7 *pa_data,
- int enable_flag,
- int block_type)
-{
- pp_sts->pa_sts = 0;
-
- if (enable_flag & MDP_PP_OPS_ENABLE)
- pp_sts->pa_sts |= PP_STS_ENABLE;
- /* Disable takes priority over all flags */
- if (enable_flag & MDP_PP_OPS_DISABLE) {
- pp_sts->pa_sts &= ~PP_STS_ENABLE;
- return;
- }
-
- if (!pa_data) {
- pr_err("PA cfg payload is null, enable flag %d\n", enable_flag);
- return;
- }
-
- /* Global HSV STS update */
- if (pa_data->mode & MDP_PP_PA_HUE_MASK)
- pp_sts->pa_sts |= PP_STS_PA_HUE_MASK;
- if (pa_data->mode & MDP_PP_PA_SAT_MASK)
- pp_sts->pa_sts |= PP_STS_PA_SAT_MASK;
- if (pa_data->mode & MDP_PP_PA_VAL_MASK)
- pp_sts->pa_sts |= PP_STS_PA_VAL_MASK;
- if (pa_data->mode & MDP_PP_PA_CONT_MASK)
- pp_sts->pa_sts |= PP_STS_PA_CONT_MASK;
- if (pa_data->mode & MDP_PP_PA_SAT_ZERO_EXP_EN)
- pp_sts->pa_sts |= PP_STS_PA_SAT_ZERO_EXP_EN;
-
- /* Memory Protect STS update */
- if (pa_data->mode & MDP_PP_PA_MEM_PROT_HUE_EN)
- pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_HUE_EN;
- if (pa_data->mode & MDP_PP_PA_MEM_PROT_SAT_EN)
- pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_SAT_EN;
- if (pa_data->mode & MDP_PP_PA_MEM_PROT_VAL_EN)
- pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_VAL_EN;
- if (pa_data->mode & MDP_PP_PA_MEM_PROT_CONT_EN)
- pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_CONT_EN;
- if (pa_data->mode & MDP_PP_PA_MEM_PROT_BLEND_EN)
- pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_BLEND_EN;
- if ((block_type == DSPP) &&
- (pa_data->mode & MDP_PP_PA_MEM_PROT_SIX_EN))
- pp_sts->pa_sts |= PP_STS_PA_MEM_PROT_SIX_EN;
-
- /* Memory Color STS update */
- if (pa_data->mode & MDP_PP_PA_MEM_COL_SKIN_MASK)
- pp_sts->pa_sts |= PP_STS_PA_MEM_COL_SKIN_MASK;
- if (pa_data->mode & MDP_PP_PA_MEM_COL_SKY_MASK)
- pp_sts->pa_sts |= PP_STS_PA_MEM_COL_SKY_MASK;
- if (pa_data->mode & MDP_PP_PA_MEM_COL_FOL_MASK)
- pp_sts->pa_sts |= PP_STS_PA_MEM_COL_FOL_MASK;
-
- /* Six Zone STS update */
- if (block_type == DSPP) {
- if (pa_data->mode & MDP_PP_PA_SIX_ZONE_HUE_MASK)
- pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_HUE_MASK;
- if (pa_data->mode & MDP_PP_PA_SIX_ZONE_SAT_MASK)
- pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_SAT_MASK;
- if (pa_data->mode & MDP_PP_PA_SIX_ZONE_VAL_MASK)
- pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_VAL_MASK;
-
- pp_sts_set_split_bits(&pp_sts->pa_sts, enable_flag);
- }
-}
-
static int pp_pa_set_config(char __iomem *base_addr,
struct pp_sts_type *pp_sts, void *cfg_data,
u32 block_type)
@@ -1763,7 +1697,7 @@ static int pp_igc_set_config(char __iomem *base_addr,
lut_cfg_data = (struct mdp_igc_lut_data *) cfg_data;
if (lut_cfg_data->version != mdp_igc_v1_7 ||
!lut_cfg_data->cfg_payload) {
- pr_err("invalid igc version %d payload %p\n",
+ pr_err_once("invalid igc version %d payload %p\n",
lut_cfg_data->version, lut_cfg_data->cfg_payload);
return -EINVAL;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c b/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c
new file mode 100644
index 000000000000..1bc803452931
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp_v3.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/uaccess.h>
+#include "mdss_fb.h"
+#include "mdss_mdp.h"
+#include "mdss_mdp_pp.h"
+#include "mdss_mdp_pp_common.h"
+
+#define IGC_DSPP_OP_MODE_EN BIT(0)
+#define ENHIST_BIT_SHIFT 16
+/* PA related define */
+
+/* Offsets from DSPP/VIG base to PA block */
+#define PA_DSPP_BLOCK_REG_OFF 0x800
+#define PA_VIG_BLOCK_REG_OFF 0x1200
+
+/* Offsets to various subblocks from PA block
+ * in VIG/DSPP.
+ */
+#define PA_OP_MODE_REG_OFF 0x0
+#define PA_HIST_REG_OFF 0x4
+#define PA_LUTV_SWAP_REG_OFF 0x18
+#define PA_HSIC_REG_OFF 0x1C
+#define PA_DITHER_CTL_REG_OFF 0x2C
+#define PA_PWL_HOLD_REG_OFF 0x40
+
+/* Memory Color offsets */
+#define PA_MEM_COL_REG_OFF 0x80
+#define PA_MEM_SKIN_REG_OFF (PA_MEM_COL_REG_OFF)
+#define PA_MEM_SKY_REG_OFF (PA_MEM_SKIN_REG_OFF + \
+ JUMP_REGISTERS_OFF(5))
+#define PA_MEM_FOL_REG_OFF (PA_MEM_SKY_REG_OFF + \
+ JUMP_REGISTERS_OFF(5))
+#define PA_MEM_SKIN_ADJUST_P2_REG_OFF (PA_MEM_FOL_REG_OFF + \
+ JUMP_REGISTERS_OFF(5))
+#define PA_MEM_SKY_ADJUST_P2_REG_OFF (PA_MEM_SKIN_ADJUST_P2_REG_OFF + \
+ JUMP_REGISTERS_OFF(2))
+#define PA_MEM_FOL_ADJUST_P2_REG_OFF (PA_MEM_SKY_ADJUST_P2_REG_OFF + \
+ JUMP_REGISTERS_OFF(2))
+
+#define PA_SZONE_REG_OFF 0x100
+#define PA_LUTV_REG_OFF 0x200
+#define PA_HIST_RAM_REG_OFF 0x400
+
+/* histogram prototypes */
+static int pp_get_hist_offset(u32 block, u32 *ctl_off);
+static int pp_hist_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type);
+static int pp_hist_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num);
+
+/* PA LUT prototypes */
+static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num);
+static int pp_hist_lut_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type);
+static int pp_hist_lut_get_version(u32 *version);
+static void pp_hist_lut_opmode_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts);
+
+static int pp_pa_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type);
+static int pp_pa_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num);
+static int pp_pa_get_version(u32 *version);
+
+static int pp_dither_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num);
+static int pp_dither_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type);
+static int pp_dither_get_version(u32 *version);
+
+static void pp_opmode_config(int location, struct pp_sts_type *pp_sts,
+ u32 *opmode, int side);
+
+static void pp_pa_set_global_adj_regs(char __iomem *base_addr,
+ struct mdp_pa_data_v1_7 *pa_data, u32 flag);
+
+static void pp_pa_set_mem_col(char __iomem *base_addr,
+ struct mdp_pa_data_v1_7 *pa_data, u32 flags);
+
+static void pp_pa_set_six_zone(char __iomem *base_addr,
+ struct mdp_pa_data_v1_7 *pa_data,
+ u32 flags);
+
+static void pp_pa_opmode_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts);
+
+void *pp_get_driver_ops_v3(struct mdp_pp_driver_ops *ops)
+{
+ void *pp_cfg = NULL;
+
+ if (!ops) {
+ pr_err("PP driver ops invalid %p\n", ops);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pp_cfg = pp_get_driver_ops_v1_7(ops);
+ if (IS_ERR_OR_NULL(pp_cfg))
+ return NULL;
+ /* PA ops */
+ ops->pp_ops[PA].pp_set_config = pp_pa_set_config;
+ ops->pp_ops[PA].pp_get_config = pp_pa_get_config;
+ ops->pp_ops[PA].pp_get_version = pp_pa_get_version;
+
+ /* HIST_LUT ops */
+ ops->pp_ops[HIST_LUT].pp_set_config = pp_hist_lut_set_config;
+ ops->pp_ops[HIST_LUT].pp_get_config = pp_hist_lut_get_config;
+ ops->pp_ops[HIST_LUT].pp_get_version = pp_hist_lut_get_version;
+
+ /* HIST ops */
+ ops->pp_ops[HIST].pp_set_config = pp_hist_set_config;
+ ops->pp_ops[HIST].pp_get_config = pp_hist_get_config;
+ ops->pp_ops[HIST].pp_get_version = NULL;
+
+ /* Dither ops */
+ ops->pp_ops[DITHER].pp_set_config = pp_dither_set_config;
+ ops->pp_ops[DITHER].pp_get_config = pp_dither_get_config;
+ ops->pp_ops[DITHER].pp_get_version = pp_dither_get_version;
+
+ /* Set opmode pointers */
+ ops->pp_opmode_config = pp_opmode_config;
+
+ ops->get_hist_offset = pp_get_hist_offset;
+ ops->gamut_clk_gate_en = NULL;
+
+ return pp_cfg;
+}
+
+static int pp_get_hist_offset(u32 block, u32 *ctl_off)
+{
+ int ret = 0;
+
+ if (!ctl_off) {
+ pr_err("invalid params ctl_off %p\n", ctl_off);
+ return -EINVAL;
+ }
+
+ switch (block) {
+ case SSPP_VIG:
+ *ctl_off = PA_VIG_BLOCK_REG_OFF + PA_HIST_REG_OFF;
+ break;
+ case DSPP:
+ *ctl_off = PA_DSPP_BLOCK_REG_OFF + PA_HIST_REG_OFF;
+ break;
+ default:
+ pr_err("Invalid block type %d\n", block);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int pp_hist_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data, u32 block_type)
+{
+ u32 opmode = 0;
+ struct pp_hist_col_info *hist_info = NULL;
+
+ if (!base_addr || !cfg_data || !pp_sts) {
+ pr_err("invalid params base_addr %p cfg_data %p pp_sts_type %p\n",
+ base_addr, cfg_data, pp_sts);
+ return -EINVAL;
+ }
+
+ if (block_type != DSPP) {
+ pr_err("Invalid block type %d\n", block_type);
+ return -EINVAL;
+ }
+
+ hist_info = (struct pp_hist_col_info *)cfg_data;
+ opmode = readl_relaxed(base_addr + PA_DSPP_BLOCK_REG_OFF +
+ PA_OP_MODE_REG_OFF);
+ /* set the hist_en bit */
+ if (hist_info->col_en) {
+ pp_sts->hist_sts |= PP_STS_ENABLE;
+ opmode |= BIT(16);
+ } else {
+ pp_sts->hist_sts &= ~PP_STS_ENABLE;
+ opmode &= ~BIT(16);
+ }
+
+ writel_relaxed(opmode, base_addr + PA_DSPP_BLOCK_REG_OFF +
+ PA_OP_MODE_REG_OFF);
+ return 0;
+}
+
+static int pp_hist_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num)
+{
+ int i = 0;
+ u32 sum = 0;
+ struct pp_hist_col_info *hist_info = NULL;
+ char __iomem *hist_addr;
+
+ if (!base_addr || !cfg_data) {
+ pr_err("invalid params base_addr %p cfg_data %p\n",
+ base_addr, cfg_data);
+ return -EINVAL;
+ }
+
+ if (block_type != DSPP) {
+ pr_err("Invalid block type %d\n", block_type);
+ return -EINVAL;
+ }
+
+ hist_info = (struct pp_hist_col_info *) cfg_data;
+ hist_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_HIST_RAM_REG_OFF;
+
+ for (i = 0; i < HIST_V_SIZE; i++) {
+ hist_info->data[i] = readl_relaxed(hist_addr) & REG_MASK(24);
+ hist_addr += 0x4;
+ sum += hist_info->data[i];
+ }
+ hist_info->hist_cnt_read++;
+ return sum;
+}
+
+static int pp_hist_lut_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num)
+{
+
+ int ret = 0, i = 0;
+ char __iomem *hist_lut_addr;
+ u32 sz = 0, temp = 0, *data = NULL;
+ struct mdp_hist_lut_data_v1_7 *lut_data = NULL;
+ struct mdp_hist_lut_data *lut_cfg_data = NULL;
+
+ if (!base_addr || !cfg_data) {
+ pr_err("invalid params base_addr %p cfg_data %p\n",
+ base_addr, cfg_data);
+ return -EINVAL;
+ }
+
+ if (block_type != DSPP) {
+ pr_err("Invalid block type %d\n", block_type);
+ return -EINVAL;
+ }
+
+ lut_cfg_data = (struct mdp_hist_lut_data *) cfg_data;
+ if (!(lut_cfg_data->ops & MDP_PP_OPS_READ)) {
+ pr_err("read ops not set for hist_lut %d\n", lut_cfg_data->ops);
+ return 0;
+ }
+ if (lut_cfg_data->version != mdp_hist_lut_v1_7 ||
+ !lut_cfg_data->cfg_payload) {
+ pr_err("invalid hist_lut version %d payload %p\n",
+ lut_cfg_data->version, lut_cfg_data->cfg_payload);
+ return -EINVAL;
+ }
+ lut_data = lut_cfg_data->cfg_payload;
+ if (lut_data->len != ENHIST_LUT_ENTRIES) {
+ pr_err("invalid hist_lut len %d", lut_data->len);
+ return -EINVAL;
+ }
+ sz = ENHIST_LUT_ENTRIES * sizeof(u32);
+ if (!access_ok(VERIFY_WRITE, lut_data->data, sz)) {
+ pr_err("invalid lut address for hist_lut sz %d\n", sz);
+ return -EFAULT;
+ }
+
+ hist_lut_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_LUTV_REG_OFF;
+
+ data = kzalloc(sz, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < ENHIST_LUT_ENTRIES; i += 2) {
+ temp = readl_relaxed(hist_lut_addr);
+ data[i] = temp & REG_MASK(10);
+ data[i + 1] =
+ (temp & REG_MASK_SHIFT(10, 16)) >> ENHIST_BIT_SHIFT;
+ hist_lut_addr += 4;
+ }
+ if (copy_to_user(lut_data->data, data, sz)) {
+ pr_err("failed to copy the hist_lut back to user\n");
+ ret = -EFAULT;
+ }
+ kfree(data);
+ return ret;
+}
+
+static int pp_hist_lut_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type)
+{
+ int ret = 0, i = 0;
+ u32 temp = 0;
+ struct mdp_hist_lut_data *lut_cfg_data = NULL;
+ struct mdp_hist_lut_data_v1_7 *lut_data = NULL;
+ char __iomem *hist_lut_addr = NULL, *swap_addr = NULL;
+
+ if (!base_addr || !cfg_data || !pp_sts) {
+ pr_err("invalid params base_addr %p cfg_data %p pp_sts_type %p\n",
+ base_addr, cfg_data, pp_sts);
+ return -EINVAL;
+ }
+
+ if (block_type != DSPP) {
+ pr_err("Invalid block type %d\n", block_type);
+ return -EINVAL;
+ }
+
+ lut_cfg_data = (struct mdp_hist_lut_data *) cfg_data;
+ if (lut_cfg_data->version != mdp_hist_lut_v1_7) {
+ pr_err("invalid hist_lut version %d\n", lut_cfg_data->version);
+ return -EINVAL;
+ }
+
+ if (!(lut_cfg_data->ops & ~(MDP_PP_OPS_READ))) {
+ pr_err("only read ops set for lut\n");
+ return ret;
+ }
+ if (lut_cfg_data->ops & MDP_PP_OPS_DISABLE ||
+ !(lut_cfg_data->ops & MDP_PP_OPS_WRITE)) {
+ pr_debug("non write ops set %d\n", lut_cfg_data->ops);
+ goto hist_lut_set_sts;
+ }
+ lut_data = lut_cfg_data->cfg_payload;
+ if (!lut_data) {
+ pr_err("invalid hist_lut cfg_payload %p\n", lut_data);
+ return -EINVAL;
+ }
+
+ if (lut_data->len != ENHIST_LUT_ENTRIES || !lut_data->data) {
+ pr_err("invalid hist_lut len %d data %p\n",
+ lut_data->len, lut_data->data);
+ return -EINVAL;
+ }
+
+ hist_lut_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_LUTV_REG_OFF;
+ swap_addr = base_addr + PA_DSPP_BLOCK_REG_OFF + PA_LUTV_SWAP_REG_OFF;
+
+ for (i = 0; i < ENHIST_LUT_ENTRIES; i += 2) {
+ temp = (lut_data->data[i] & REG_MASK(10)) |
+ ((lut_data->data[i + 1] & REG_MASK(10))
+ << ENHIST_BIT_SHIFT);
+
+ writel_relaxed(temp, hist_lut_addr);
+ hist_lut_addr += 4;
+ }
+
+ writel_relaxed(1, swap_addr);
+
+hist_lut_set_sts:
+ if (lut_cfg_data->ops & MDP_PP_OPS_DISABLE) {
+ pp_sts->enhist_sts &= ~(PP_STS_ENABLE | PP_STS_PA_LUT_FIRST);
+ } else if (lut_cfg_data->ops & MDP_PP_OPS_ENABLE) {
+ pp_sts->enhist_sts |= PP_STS_ENABLE;
+ if (lut_cfg_data->hist_lut_first)
+ pp_sts->enhist_sts |= PP_STS_PA_LUT_FIRST;
+ else
+ pp_sts->enhist_sts &= ~PP_STS_PA_LUT_FIRST;
+ }
+
+ pp_hist_lut_opmode_config(base_addr + PA_DSPP_BLOCK_REG_OFF, pp_sts);
+ return ret;
+}
+
+static int pp_hist_lut_get_version(u32 *version)
+{
+ if (!version) {
+ pr_err("invalid param version %p\n", version);
+ return -EINVAL;
+ }
+ *version = mdp_hist_lut_v1_7;
+ return 0;
+}
+
+static void pp_hist_lut_opmode_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts)
+{
+ u32 opmode = 0;
+
+ if (!base_addr || !pp_sts) {
+ pr_err("invalid params base_addr %p pp_sts_type %p\n",
+ base_addr, pp_sts);
+ return;
+ }
+ opmode = readl_relaxed(base_addr + PA_OP_MODE_REG_OFF);
+
+ /* set the hist_lutv_en and hist_lutv_first_en bits */
+ if (pp_sts->enhist_sts & PP_STS_ENABLE) {
+ opmode |= BIT(19) | BIT(20);
+ opmode |= (pp_sts->enhist_sts & PP_STS_PA_LUT_FIRST) ?
+ BIT(21) : 0;
+ } else {
+ opmode &= ~(BIT(19) | BIT(21));
+ if (!(pp_sts->pa_sts & PP_STS_ENABLE))
+ opmode &= ~BIT(20);
+ }
+
+ writel_relaxed(opmode, base_addr + PA_OP_MODE_REG_OFF);
+}
+
+static int pp_pa_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type)
+{
+ struct mdp_pa_v2_cfg_data *pa_cfg_data = NULL;
+ struct mdp_pa_data_v1_7 *pa_data = NULL;
+ char __iomem *block_addr = NULL;
+
+ if (!base_addr || !cfg_data || !pp_sts) {
+ pr_err("invalid params base_addr %p cfg_data %p pp_sts_type %p\n",
+ base_addr, cfg_data, pp_sts);
+ return -EINVAL;
+ }
+ if ((block_type != DSPP) && (block_type != SSPP_VIG)) {
+ pr_err("Invalid block type %d\n", block_type);
+ return -EINVAL;
+ }
+
+ pa_cfg_data = (struct mdp_pa_v2_cfg_data *) cfg_data;
+ if (pa_cfg_data->version != mdp_pa_v1_7) {
+ pr_err("invalid pa version %d\n", pa_cfg_data->version);
+ return -EINVAL;
+ }
+ if (!(pa_cfg_data->flags & ~(MDP_PP_OPS_READ))) {
+ pr_info("only read ops is set %d", pa_cfg_data->flags);
+ return 0;
+ }
+
+ block_addr = base_addr +
+ ((block_type == DSPP) ? PA_DSPP_BLOCK_REG_OFF :
+ PA_VIG_BLOCK_REG_OFF);
+
+ if (pa_cfg_data->flags & MDP_PP_OPS_DISABLE ||
+ !(pa_cfg_data->flags & MDP_PP_OPS_WRITE)) {
+ pr_debug("pa_cfg_data->flags = %d\n", pa_cfg_data->flags);
+ goto pa_set_sts;
+ }
+
+ pa_data = pa_cfg_data->cfg_payload;
+ if (!pa_data) {
+ pr_err("invalid payload for pa %p\n", pa_data);
+ return -EINVAL;
+ }
+
+ pp_pa_set_global_adj_regs(block_addr, pa_data, pa_cfg_data->flags);
+ pp_pa_set_mem_col(block_addr, pa_data, pa_cfg_data->flags);
+ if (block_type == DSPP)
+ pp_pa_set_six_zone(block_addr, pa_data, pa_cfg_data->flags);
+
+pa_set_sts:
+ pp_pa_set_sts(pp_sts, pa_data, pa_cfg_data->flags, block_type);
+ pp_pa_opmode_config(block_addr, pp_sts);
+
+ return 0;
+}
+
+static int pp_pa_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num)
+{
+ return -EINVAL;
+}
+
+static int pp_pa_get_version(u32 *version)
+{
+ if (!version) {
+ pr_err("invalid param version");
+ return -EINVAL;
+ }
+ *version = mdp_pa_v1_7;
+ return 0;
+}
+
+static int pp_dither_get_config(char __iomem *base_addr, void *cfg_data,
+ u32 block_type, u32 disp_num)
+{
+ return -EINVAL;
+}
+
+static int pp_dither_set_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts, void *cfg_data,
+ u32 block_type)
+{
+ return -EINVAL;
+}
+
+static int pp_dither_get_version(u32 *version)
+{
+ if (!version) {
+ pr_err("invalid param version");
+ return -EINVAL;
+ }
+ *version = mdp_dither_v1_7;
+ return 0;
+}
+
+static void pp_opmode_config(int location, struct pp_sts_type *pp_sts,
+ u32 *opmode, int side)
+{
+ if (!pp_sts || !opmode) {
+ pr_err("Invalid pp_sts %p or opmode %p\n", pp_sts, opmode);
+ return;
+ }
+ switch (location) {
+ case SSPP_DMA:
+ break;
+ case SSPP_VIG:
+ break;
+ case DSPP:
+ if (pp_sts_is_enabled(pp_sts->igc_sts, side))
+ *opmode |= IGC_DSPP_OP_MODE_EN;
+ break;
+ case LM:
+ if (pp_sts->argc_sts & PP_STS_ENABLE)
+ pr_debug("pgc in LM enabled\n");
+ break;
+ default:
+ pr_err("Invalid block type %d\n", location);
+ break;
+ }
+}
+
+static void pp_pa_set_global_adj_regs(char __iomem *base_addr,
+ struct mdp_pa_data_v1_7 *pa_data, u32 flags)
+{
+ char __iomem *addr = NULL;
+
+ addr = base_addr + PA_HSIC_REG_OFF;
+ if (flags & MDP_PP_PA_HUE_ENABLE)
+ writel_relaxed((pa_data->global_hue_adj &
+ REG_MASK(12)), addr);
+ addr += 4;
+ if (flags & MDP_PP_PA_SAT_ENABLE)
+ writel_relaxed((pa_data->global_sat_adj &
+ REG_MASK(16)), addr);
+ addr += 4;
+ if (flags & MDP_PP_PA_VAL_ENABLE)
+ writel_relaxed((pa_data->global_val_adj &
+ REG_MASK(8)), addr);
+ addr += 4;
+ if (flags & MDP_PP_PA_CONT_ENABLE)
+ writel_relaxed((pa_data->global_cont_adj &
+ REG_MASK(8)), addr);
+}
+
+static void pp_pa_set_mem_col(char __iomem *base_addr,
+ struct mdp_pa_data_v1_7 *pa_data, u32 flags)
+{
+ char __iomem *mem_col_base = NULL, *mem_col_p2 = NULL;
+ struct mdp_pa_mem_col_data_v1_7 *mem_col_data = NULL;
+ uint32_t mask = 0, hold = 0, hold_mask = 0;
+ uint32_t hold_curr = 0;
+
+ flags &= (MDP_PP_PA_SKIN_ENABLE | MDP_PP_PA_SKY_ENABLE |
+ MDP_PP_PA_FOL_ENABLE);
+ if (!flags)
+ return;
+ while (flags) {
+ if (flags & MDP_PP_PA_SKIN_ENABLE) {
+ flags &= ~MDP_PP_PA_SKIN_ENABLE;
+ mem_col_base = base_addr + PA_MEM_SKIN_REG_OFF;
+ mem_col_p2 = base_addr + PA_MEM_SKIN_ADJUST_P2_REG_OFF;
+ mem_col_data = &pa_data->skin_cfg;
+ hold |= pa_data->skin_cfg.sat_hold & REG_MASK(2);
+ hold |= (pa_data->skin_cfg.val_hold & REG_MASK(2))
+ << 2;
+ hold_mask |= REG_MASK(4);
+ } else if (flags & MDP_PP_PA_SKY_ENABLE) {
+ flags &= ~MDP_PP_PA_SKY_ENABLE;
+ mem_col_base = base_addr + PA_MEM_SKY_REG_OFF;
+ mem_col_p2 = base_addr + PA_MEM_SKY_ADJUST_P2_REG_OFF;
+ mem_col_data = &pa_data->sky_cfg;
+ hold |= (pa_data->sky_cfg.sat_hold & REG_MASK(2)) << 4;
+ hold |= (pa_data->sky_cfg.val_hold & REG_MASK(2)) << 6;
+ hold_mask |= REG_MASK_SHIFT(4, 4);
+ } else if (flags & MDP_PP_PA_FOL_ENABLE) {
+ flags &= ~MDP_PP_PA_FOL_ENABLE;
+ mem_col_base = base_addr + PA_MEM_FOL_REG_OFF;
+ mem_col_p2 = base_addr + PA_MEM_FOL_ADJUST_P2_REG_OFF;
+ mem_col_data = &pa_data->fol_cfg;
+ hold |= (pa_data->fol_cfg.sat_hold & REG_MASK(2)) << 8;
+ hold |= (pa_data->fol_cfg.val_hold & REG_MASK(2)) << 10;
+ hold_mask |= REG_MASK_SHIFT(4, 8);
+ } else {
+ break;
+ }
+ mask = REG_MASK_SHIFT(16, 16) | REG_MASK(11);
+ writel_relaxed((mem_col_data->color_adjust_p0 & mask),
+ mem_col_base);
+ mem_col_base += 4;
+ mask = U32_MAX;
+ writel_relaxed((mem_col_data->color_adjust_p1 & mask),
+ mem_col_base);
+ mem_col_base += 4;
+ mask = REG_MASK_SHIFT(11, 16) | REG_MASK(11);
+ writel_relaxed((mem_col_data->hue_region & mask),
+ mem_col_base);
+ mem_col_base += 4;
+ mask = REG_MASK(24);
+ writel_relaxed((mem_col_data->sat_region & mask),
+ mem_col_base);
+ mem_col_base += 4;
+ /* mask is same for val and sat */
+ writel_relaxed((mem_col_data->val_region & mask),
+ mem_col_base);
+ mask = U32_MAX;
+ writel_relaxed((mem_col_data->color_adjust_p2 & mask),
+ mem_col_p2);
+ mem_col_p2 += 4;
+ writel_relaxed((mem_col_data->blend_gain & mask),
+ mem_col_p2);
+ }
+ hold_curr = readl_relaxed(base_addr + PA_PWL_HOLD_REG_OFF) &
+ REG_MASK(16);
+ hold_curr &= ~hold_mask;
+ hold = hold_curr | (hold & hold_mask);
+ writel_relaxed(hold, (base_addr + PA_PWL_HOLD_REG_OFF));
+}
+
+static void pp_pa_set_six_zone(char __iomem *base_addr,
+ struct mdp_pa_data_v1_7 *pa_data,
+ u32 flags)
+{
+ char __iomem *addr = base_addr + PA_SZONE_REG_OFF;
+ uint32_t mask_p0 = 0, mask_p1 = 0, hold = 0, hold_mask = 0;
+ uint32_t hold_curr = 0;
+ int i = 0;
+
+ if (!(flags & MDP_PP_PA_SIX_ZONE_ENABLE))
+ return;
+
+ if (pa_data->six_zone_len != MDP_SIX_ZONE_LUT_SIZE ||
+ !pa_data->six_zone_curve_p0 ||
+ !pa_data->six_zone_curve_p1) {
+ pr_err("Invalid six zone data: len %d curve_p0 %p curve_p1 %p\n",
+ pa_data->six_zone_len,
+ pa_data->six_zone_curve_p0,
+ pa_data->six_zone_curve_p1);
+ return;
+ }
+ mask_p0 = REG_MASK(12);
+ mask_p1 = REG_MASK(12) | REG_MASK_SHIFT(12, 16);
+ writel_relaxed((pa_data->six_zone_curve_p1[0] & mask_p1), addr + 4);
+ /* Update the index to 0 and write value */
+ writel_relaxed((pa_data->six_zone_curve_p0[0] & mask_p0) | BIT(26),
+ addr);
+ for (i = 1; i < MDP_SIX_ZONE_LUT_SIZE; i++) {
+ writel_relaxed((pa_data->six_zone_curve_p1[i] & mask_p1),
+ addr + 4);
+ writel_relaxed((pa_data->six_zone_curve_p0[i] & mask_p0), addr);
+ }
+ addr += 8;
+ writel_relaxed(pa_data->six_zone_thresh, addr);
+ addr += 4;
+ writel_relaxed(pa_data->six_zone_adj_p0 & REG_MASK(16), addr);
+ addr += 4;
+ writel_relaxed(pa_data->six_zone_adj_p1, addr);
+
+ hold = (pa_data->six_zone_sat_hold & REG_MASK(2)) << 12;
+ hold |= (pa_data->six_zone_val_hold & REG_MASK(2)) << 14;
+ hold_mask = REG_MASK_SHIFT(4, 12);
+ hold_curr = readl_relaxed(base_addr + PA_PWL_HOLD_REG_OFF) &
+ REG_MASK(16);
+ hold_curr &= ~hold_mask;
+ hold = hold_curr | (hold & hold_mask);
+ writel_relaxed(hold, (base_addr + PA_PWL_HOLD_REG_OFF));
+}
+
+static void pp_pa_opmode_config(char __iomem *base_addr,
+ struct pp_sts_type *pp_sts)
+{
+ uint32_t opmode = 0;
+
+ /* set the PA bits */
+ if (pp_sts->pa_sts & PP_STS_ENABLE) {
+ opmode |= BIT(20);
+
+ if (pp_sts->pa_sts & PP_STS_PA_HUE_MASK)
+ opmode |= BIT(25);
+ if (pp_sts->pa_sts & PP_STS_PA_SAT_MASK)
+ opmode |= BIT(26);
+ if (pp_sts->pa_sts & PP_STS_PA_VAL_MASK)
+ opmode |= BIT(27);
+ if (pp_sts->pa_sts & PP_STS_PA_CONT_MASK)
+ opmode |= BIT(28);
+ if (pp_sts->pa_sts & PP_STS_PA_SAT_ZERO_EXP_EN)
+ opmode |= BIT(1);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_COL_SKIN_MASK)
+ opmode |= BIT(5);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_COL_FOL_MASK)
+ opmode |= BIT(6);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_COL_SKY_MASK)
+ opmode |= BIT(7);
+ if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_HUE_MASK)
+ opmode |= BIT(29);
+ if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_SAT_MASK)
+ opmode |= BIT(30);
+ if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_VAL_MASK)
+ opmode |= BIT(31);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_HUE_EN)
+ opmode |= BIT(22);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_SAT_EN)
+ opmode |= BIT(23);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_VAL_EN)
+ opmode |= BIT(24);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_CONT_EN)
+ opmode |= BIT(18);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_BLEND_EN)
+ opmode |= BIT(3);
+ if (pp_sts->pa_sts & PP_STS_PA_MEM_PROT_SIX_EN)
+ opmode |= BIT(17);
+ }
+
+ /* reset hist_en, hist_lutv_en and hist_lutv_first_en
+ bits based on the pp_sts
+ */
+ if (pp_sts->hist_sts & PP_STS_ENABLE)
+ opmode |= BIT(16);
+ if (pp_sts->enhist_sts & PP_STS_ENABLE)
+ opmode |= BIT(19) | BIT(20);
+ if (pp_sts->enhist_sts & PP_STS_PA_LUT_FIRST)
+ opmode |= BIT(21);
+
+ writel_relaxed(opmode, base_addr + PA_OP_MODE_REG_OFF);
+}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index c86f924d83db..d85d85204cb2 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -49,6 +49,10 @@ enum {
MDP_INTR_PING_PONG_1_RD_PTR,
MDP_INTR_PING_PONG_2_RD_PTR,
MDP_INTR_PING_PONG_3_RD_PTR,
+ MDP_INTR_PING_PONG_0_WR_PTR,
+ MDP_INTR_PING_PONG_1_WR_PTR,
+ MDP_INTR_PING_PONG_2_WR_PTR,
+ MDP_INTR_PING_PONG_3_WR_PTR,
MDP_INTR_WB_0,
MDP_INTR_WB_1,
MDP_INTR_WB_2,
@@ -83,6 +87,9 @@ static int mdss_mdp_intr2index(u32 intr_type, u32 intf_num)
case MDSS_MDP_IRQ_PING_PONG_RD_PTR:
index = MDP_INTR_PING_PONG_0_RD_PTR + intf_num;
break;
+ case MDSS_MDP_IRQ_PING_PONG_WR_PTR:
+ index = MDP_INTR_PING_PONG_0_WR_PTR + intf_num;
+ break;
case MDSS_MDP_IRQ_WB_ROT_COMP:
index = MDP_INTR_WB_0 + intf_num;
break;
@@ -216,6 +223,18 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr)
if (isr & MDSS_MDP_INTR_PING_PONG_3_RD_PTR)
mdss_mdp_intr_done(MDP_INTR_PING_PONG_3_RD_PTR);
+ if (isr & MDSS_MDP_INTR_PING_PONG_0_WR_PTR)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_0_WR_PTR);
+
+ if (isr & MDSS_MDP_INTR_PING_PONG_1_WR_PTR)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_1_WR_PTR);
+
+ if (isr & MDSS_MDP_INTR_PING_PONG_2_WR_PTR)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_2_WR_PTR);
+
+ if (isr & MDSS_MDP_INTR_PING_PONG_3_WR_PTR)
+ mdss_mdp_intr_done(MDP_INTR_PING_PONG_3_WR_PTR);
+
if (isr & MDSS_MDP_INTR_INTF_0_VSYNC) {
mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);
mdss_misr_crc_collect(mdata, DISPLAY_MISR_EDP, true);
@@ -1218,6 +1237,10 @@ static int mdss_mdp_put_img(struct mdss_mdp_img_data *data, bool rotator,
/*
* skip memory unmapping - secure display uses physical
* address which does not require buffer unmapping
+ *
+ * For LT targets in secure display usecase, srcp_dma_buf will
+ * be filled due to map call which will be unmapped above.
+ *
*/
pr_debug("skip memory unmapping for secure display content\n");
} else {
@@ -1238,6 +1261,7 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
u32 domain;
dma_addr_t *start;
struct ion_client *iclient = mdss_get_ionclient();
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
start = &data->addr;
len = &data->len;
@@ -1261,84 +1285,85 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
pr_err("invalid FB_MAJOR\n");
ret = -1;
}
- } else if (iclient &&
- !(data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)) {
- data->srcp_dma_buf = dma_buf_get(img->memory_id);
- if (IS_ERR(data->srcp_dma_buf)) {
- pr_err("error on ion_import_fd\n");
- ret = PTR_ERR(data->srcp_dma_buf);
- data->srcp_dma_buf = NULL;
- return ret;
- }
- domain = mdss_smmu_get_domain_type(data->flags, rotator);
-
- data->srcp_attachment =
- mdss_smmu_dma_buf_attach(data->srcp_dma_buf, dev,
- domain);
- if (IS_ERR(data->srcp_attachment)) {
- ret = PTR_ERR(data->srcp_attachment);
- goto err_put;
- }
-
- data->srcp_table =
- dma_buf_map_attachment(data->srcp_attachment,
- mdss_smmu_dma_data_direction(dir));
- if (IS_ERR(data->srcp_table)) {
- ret = PTR_ERR(data->srcp_table);
- goto err_detach;
- }
-
- data->addr = 0;
- data->len = 0;
- data->mapped = false;
- data->skip_detach = false;
- /* return early, mapping will be done later */
-
- return 0;
- } else if (iclient &&
- (data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)) {
- struct ion_handle *ihandle = NULL;
- struct sg_table *sg_ptr = NULL;
-
- do {
- ihandle = ion_import_dma_buf(iclient, img->memory_id);
- if (IS_ERR_OR_NULL(ihandle)) {
- ret = -EINVAL;
- pr_err("ion import buffer failed\n");
- break;
- }
-
- sg_ptr = ion_sg_table(iclient, ihandle);
- if (sg_ptr == NULL) {
- pr_err("ion sg table get failed\n");
- ret = -EINVAL;
- break;
+ } else if (iclient) {
+ if (mdss_mdp_is_map_needed(mdata, data)) {
+ data->srcp_dma_buf = dma_buf_get(img->memory_id);
+ if (IS_ERR(data->srcp_dma_buf)) {
+ pr_err("error on ion_import_fd\n");
+ ret = PTR_ERR(data->srcp_dma_buf);
+ data->srcp_dma_buf = NULL;
+ return ret;
}
-
- if (sg_ptr->nents != 1) {
- pr_err("ion buffer mapping failed\n");
- ret = -EINVAL;
- break;
+ domain = mdss_smmu_get_domain_type(data->flags,
+ rotator);
+
+ data->srcp_attachment =
+ mdss_smmu_dma_buf_attach(data->srcp_dma_buf,
+ dev, domain);
+ if (IS_ERR(data->srcp_attachment)) {
+ ret = PTR_ERR(data->srcp_attachment);
+ goto err_put;
}
- if (((uint64_t)sg_dma_address(sg_ptr->sgl) >=
- PHY_ADDR_4G - sg_ptr->sgl->length)) {
- pr_err("ion buffer mapped size is invalid\n");
- ret = -EINVAL;
- break;
+ data->srcp_table =
+ dma_buf_map_attachment(data->srcp_attachment,
+ mdss_smmu_dma_data_direction(dir));
+ if (IS_ERR(data->srcp_table)) {
+ ret = PTR_ERR(data->srcp_table);
+ goto err_detach;
}
- data->addr = sg_dma_address(sg_ptr->sgl);
- data->len = sg_ptr->sgl->length;
- data->mapped = true;
+ data->addr = 0;
+ data->len = 0;
+ data->mapped = false;
+ data->skip_detach = false;
+ /* return early, mapping will be done later */
ret = 0;
- } while (0);
-
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(iclient, ihandle);
- return ret;
+ goto done;
+ } else {
+ struct ion_handle *ihandle = NULL;
+ struct sg_table *sg_ptr = NULL;
+
+ do {
+ ihandle = ion_import_dma_buf(iclient,
+ img->memory_id);
+ if (IS_ERR_OR_NULL(ihandle)) {
+ ret = -EINVAL;
+ pr_err("ion import buffer failed\n");
+ break;
+ }
+
+ sg_ptr = ion_sg_table(iclient, ihandle);
+ if (sg_ptr == NULL) {
+ pr_err("ion sg table get failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (sg_ptr->nents != 1) {
+ pr_err("ion buffer mapping failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (((uint64_t)sg_dma_address(sg_ptr->sgl) >=
+ PHY_ADDR_4G - sg_ptr->sgl->length)) {
+ pr_err("ion buffer mapped size is invalid\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ data->addr = sg_dma_address(sg_ptr->sgl);
+ data->len = sg_ptr->sgl->length;
+ data->mapped = true;
+ ret = 0;
+ } while (0);
+
+ if (!IS_ERR_OR_NULL(ihandle))
+ ion_free(iclient, ihandle);
+ return ret;
+ }
}
-
if (!*start) {
pr_err("start address is zero!\n");
mdss_mdp_put_img(data, rotator, dir);
@@ -1361,6 +1386,7 @@ err_detach:
dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
err_put:
dma_buf_put(data->srcp_dma_buf);
+done:
return ret;
}
@@ -1369,13 +1395,14 @@ static int mdss_mdp_map_buffer(struct mdss_mdp_img_data *data, bool rotator,
{
int ret = -EINVAL;
int domain;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
if (data->addr && data->len)
return 0;
if (!IS_ERR_OR_NULL(data->srcp_dma_buf)) {
if (mdss_res->mdss_util->iommu_attached() &&
- !(data->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION)) {
+ (mdss_mdp_is_map_needed(mdata, data))) {
domain = mdss_smmu_get_domain_type(data->flags,
rotator);
data->dir = dir;
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 83949d821fdb..33d8d7bd77bd 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2016, 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
@@ -158,6 +158,11 @@ struct mdss_panel_cfg {
#define MDP_INTF_DSI_CMD_FIFO_UNDERFLOW 0x0001
#define MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW 0x0002
+
+enum {
+ MDP_INTF_CALLBACK_DSI_WAIT,
+};
+
struct mdss_intf_recovery {
void (*fxn)(void *ctx, int event);
void *data;
@@ -210,6 +215,7 @@ struct mdss_intf_recovery {
* - 1: update to command mode
* @MDSS_EVENT_REGISTER_RECOVERY_HANDLER: Event to recover the interface in
* case there was any errors detected.
+ * @MDSS_EVENT_REGISTER_MDP_CALLBACK: Event to register callback to MDP driver.
* @MDSS_EVENT_DSI_PANEL_STATUS: Event to check the panel status
* <= 0: panel check fail
* > 0: panel check success
@@ -248,6 +254,7 @@ enum mdss_intf_events {
MDSS_EVENT_DSI_STREAM_SIZE,
MDSS_EVENT_DSI_UPDATE_PANEL_DATA,
MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
MDSS_EVENT_DSI_PANEL_STATUS,
MDSS_EVENT_DSI_DYNAMIC_SWITCH,
MDSS_EVENT_DSI_RECONFIG_CMD,
@@ -531,6 +538,7 @@ struct mdss_mdp_pp_tear_check {
u32 sync_threshold_continue;
u32 start_pos;
u32 rd_ptr_irq;
+ u32 wr_ptr_irq;
u32 refx100;
};
@@ -599,7 +607,6 @@ struct mdss_panel_info {
u32 partial_update_roi_merge;
struct ion_handle *splash_ihdl;
int panel_power_state;
- int blank_state;
int compression_mode;
uint32_t panel_dead;
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 6b9e8a76f406..6aba03ea2570 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, 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
@@ -375,7 +375,7 @@ static bool mdss_rotator_is_work_pending(struct mdss_rot_mgr *mgr,
static int mdss_rotator_create_fence(struct mdss_rot_entry *entry)
{
- int ret, fd;
+ int ret = 0, fd;
u32 val;
struct sync_pt *sync_pt;
struct sync_fence *fence;
@@ -1123,6 +1123,7 @@ static void mdss_rotator_release_from_work_distribution(
entry->perf->work_distribution);
devm_kfree(&mgr->pdev->dev, entry->perf);
mdss_rotator_update_perf(mgr);
+ mdss_rotator_clk_ctrl(mgr, false);
entry->perf = NULL;
}
}
@@ -1700,11 +1701,13 @@ static int mdss_rotator_config_hw(struct mdss_rot_hw_resource *hw,
{
struct mdss_mdp_pipe *pipe;
struct mdp_rotation_item *item;
+ struct mdss_rot_perf *perf;
int ret;
ATRACE_BEGIN(__func__);
pipe = hw->pipe;
item = &entry->item;
+ perf = entry->perf;
pipe->flags = mdss_rotator_translate_flags(item->flags);
pipe->src_fmt = mdss_mdp_get_format_params(item->input.format);
@@ -1713,6 +1716,7 @@ static int mdss_rotator_config_hw(struct mdss_rot_hw_resource *hw,
mdss_rotator_translate_rect(&pipe->src, &item->src_rect);
mdss_rotator_translate_rect(&pipe->dst, &item->src_rect);
pipe->scaler.enable = 0;
+ pipe->frame_rate = perf->config.frame_rate;
pipe->params_changed++;
@@ -2142,6 +2146,7 @@ static int mdss_rotator_handle_request(struct mdss_rot_mgr *mgr,
struct mdp_rotation_item *items = NULL;
struct mdss_rot_entry_container *req = NULL;
int size, ret;
+ uint32_t req_count;
if (mdss_get_sd_client_cnt()) {
pr_err("rot request not permitted during secure display session\n");
@@ -2155,12 +2160,18 @@ static int mdss_rotator_handle_request(struct mdss_rot_mgr *mgr,
return ret;
}
+ req_count = user_req.count;
+ if ((!req_count) || (req_count > MAX_LAYER_COUNT)) {
+ pr_err("invalid rotator req count :%d\n", req_count);
+ return -EINVAL;
+ }
+
/*
* here, we make a copy of the items so that we can copy
* all the output fences to the client in one call. Otherwise,
* we will have to call multiple copy_to_user
*/
- size = sizeof(struct mdp_rotation_item) * user_req.count;
+ size = sizeof(struct mdp_rotation_item) * req_count;
items = devm_kzalloc(&mgr->pdev->dev, size, GFP_KERNEL);
if (!items) {
pr_err("fail to allocate rotation items\n");
@@ -2299,6 +2310,7 @@ static int mdss_rotator_handle_request32(struct mdss_rot_mgr *mgr,
struct mdp_rotation_item *items = NULL;
struct mdss_rot_entry_container *req = NULL;
int size, ret;
+ uint32_t req_count;
if (mdss_get_sd_client_cnt()) {
pr_err("rot request not permitted during secure display session\n");
@@ -2312,13 +2324,19 @@ static int mdss_rotator_handle_request32(struct mdss_rot_mgr *mgr,
return ret;
}
- size = sizeof(struct mdp_rotation_item) * user_req32.count;
+ req_count = user_req32.count;
+ if ((!req_count) || (req_count > MAX_LAYER_COUNT)) {
+ pr_err("invalid rotator req count :%d\n", req_count);
+ return -EINVAL;
+ }
+
+ size = sizeof(struct mdp_rotation_item) * req_count;
items = devm_kzalloc(&mgr->pdev->dev, size, GFP_KERNEL);
if (!items) {
pr_err("fail to allocate rotation items\n");
return -ENOMEM;
}
- ret = copy_from_user(items, user_req32.list, size);
+ ret = copy_from_user(items, compat_ptr(user_req32.list), size);
if (ret) {
pr_err("fail to copy rotation items\n");
goto handle_request32_err;
@@ -2345,7 +2363,7 @@ static int mdss_rotator_handle_request32(struct mdss_rot_mgr *mgr,
goto handle_request32_err1;
}
- ret = copy_to_user(user_req32.list, items, size);
+ ret = copy_to_user(compat_ptr(user_req32.list), items, size);
if (ret) {
pr_err("fail to copy output fence to user\n");
mdss_rotator_remove_request(mgr, private, req);
@@ -2503,6 +2521,7 @@ static const struct file_operations mdss_rotator_fops = {
static int mdss_rotator_parse_dt_bus(struct mdss_rot_mgr *mgr,
struct platform_device *dev)
{
+ struct device_node *node;
int ret = 0, i;
bool register_bus_needed;
int usecases;
@@ -2520,12 +2539,26 @@ static int mdss_rotator_parse_dt_bus(struct mdss_rot_mgr *mgr,
register_bus_needed = of_property_read_bool(dev->dev.of_node,
"qcom,mdss-has-reg-bus");
if (register_bus_needed) {
- mgr->reg_bus.bus_scale_pdata = &rot_reg_bus_scale_table;
- usecases = mgr->reg_bus.bus_scale_pdata->num_usecases;
- for (i = 0; i < usecases; i++) {
- rot_reg_bus_usecases[i].num_paths = 1;
- rot_reg_bus_usecases[i].vectors =
- &rot_reg_bus_vectors[i];
+ node = of_get_child_by_name(
+ dev->dev.of_node, "qcom,mdss-rot-reg-bus");
+ if (!node) {
+ mgr->reg_bus.bus_scale_pdata = &rot_reg_bus_scale_table;
+ usecases = mgr->reg_bus.bus_scale_pdata->num_usecases;
+ for (i = 0; i < usecases; i++) {
+ rot_reg_bus_usecases[i].num_paths = 1;
+ rot_reg_bus_usecases[i].vectors =
+ &rot_reg_bus_vectors[i];
+ }
+ } else {
+ mgr->reg_bus.bus_scale_pdata =
+ msm_bus_pdata_from_node(dev, node);
+ if (IS_ERR_OR_NULL(mgr->reg_bus.bus_scale_pdata)) {
+ ret = PTR_ERR(mgr->reg_bus.bus_scale_pdata);
+ if (!ret)
+ ret = -EINVAL;
+ pr_err("reg_rot_bus failed rc=%d\n", ret);
+ mgr->reg_bus.bus_scale_pdata = NULL;
+ }
}
}
return ret;
diff --git a/drivers/video/fbdev/msm/mdss_rotator_internal.h b/drivers/video/fbdev/msm/mdss_rotator_internal.h
index 87c5dcd98813..dae5f5cb117e 100644
--- a/drivers/video/fbdev/msm/mdss_rotator_internal.h
+++ b/drivers/video/fbdev/msm/mdss_rotator_internal.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, 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
@@ -191,7 +191,7 @@ struct mdp_rotation_request32 {
uint32_t version;
uint32_t flags;
uint32_t count;
- compat_caddr_t __user *list;
+ compat_caddr_t list;
uint32_t reserved[6];
};
#endif
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index c60074e76bf2..9aa2c8386b17 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2007-2016, 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
@@ -138,7 +138,7 @@ static int mdss_smmu_enable_power(struct mdss_smmu_client *mdss_smmu,
goto end;
}
mdss_update_reg_bus_vote(mdss_smmu->reg_bus_clt,
- VOTE_INDEX_19_MHZ);
+ VOTE_INDEX_LOW);
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true);
if (rc) {
pr_err("clock enable failed - rc:%d\n", rc);
@@ -604,7 +604,7 @@ int mdss_smmu_probe(struct platform_device *pdev)
snprintf(name, MAX_CLIENT_NAME_LEN, "smmu:%u", smmu_domain.domain);
mdss_smmu->reg_bus_clt = mdss_reg_bus_vote_client_create(name);
- if (IS_ERR_OR_NULL(mdss_smmu->reg_bus_clt)) {
+ if (IS_ERR(mdss_smmu->reg_bus_clt)) {
pr_err("mdss bus client register failed\n");
msm_dss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg,
false);
diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c
index e27f3b97471a..a3b4466d105d 100644
--- a/drivers/video/fbdev/msm/msm_dba/adv7533.c
+++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c
@@ -38,6 +38,8 @@
#define MDSS_MAX_PANEL_LEN 256
#define EDID_SEG_SIZE 0x100
+/* size of audio and speaker info Block */
+#define AUDIO_DATA_SIZE 32
/* 0x94 interrupts */
#define HPD_INT_ENABLE BIT(7)
@@ -129,6 +131,7 @@ struct adv7533 {
bool is_power_on;
void *edid_data;
u8 edid_buf[EDID_SEG_SIZE];
+ u8 audio_spkr_data[AUDIO_DATA_SIZE];
struct workqueue_struct *workq;
struct delayed_work adv7533_intr_work_id;
struct msm_dba_device_info dev_info;
@@ -1274,6 +1277,44 @@ static int adv7533_cec_enable(void *client, bool cec_on, u32 flags)
end:
return ret;
}
+static void adv7533_set_audio_block(void *client, u32 size, void *buf)
+{
+ struct adv7533 *pdata =
+ adv7533_get_platform_data(client);
+
+ if (!pdata || !buf) {
+ pr_err("%s: invalid data\n", __func__);
+ return;
+ }
+
+ mutex_lock(&pdata->ops_mutex);
+
+ size = min_t(u32, size, AUDIO_DATA_SIZE);
+
+ memset(pdata->audio_spkr_data, 0, AUDIO_DATA_SIZE);
+ memcpy(pdata->audio_spkr_data, buf, size);
+
+ mutex_unlock(&pdata->ops_mutex);
+}
+
+static void adv7533_get_audio_block(void *client, u32 size, void *buf)
+{
+ struct adv7533 *pdata =
+ adv7533_get_platform_data(client);
+
+ if (!pdata || !buf) {
+ pr_err("%s: invalid data\n", __func__);
+ return;
+ }
+
+ mutex_lock(&pdata->ops_mutex);
+
+ size = min_t(u32, size, AUDIO_DATA_SIZE);
+
+ memcpy(buf, pdata->audio_spkr_data, size);
+
+ mutex_unlock(&pdata->ops_mutex);
+}
static int adv7533_check_hpd(void *client, u32 flags)
{
@@ -1880,6 +1921,8 @@ static int adv7533_register_dba(struct adv7533 *pdata)
client_ops->get_edid_size = adv7533_get_edid_size;
client_ops->get_raw_edid = adv7533_get_raw_edid;
client_ops->check_hpd = adv7533_check_hpd;
+ client_ops->get_audio_block = adv7533_get_audio_block;
+ client_ops->set_audio_block = adv7533_set_audio_block;
dev_ops->write_reg = adv7533_write_reg;
dev_ops->read_reg = adv7533_read_reg;
@@ -1888,8 +1931,6 @@ static int adv7533_register_dba(struct adv7533 *pdata)
strlcpy(pdata->dev_info.chip_name, "adv7533",
sizeof(pdata->dev_info.chip_name));
- pdata->dev_info.instance_id = 0;
-
mutex_init(&pdata->dev_info.dev_mutex);
INIT_LIST_HEAD(&pdata->dev_info.client_list);
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index 7cc2717a6df5..b23e24362af6 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -53,6 +53,9 @@
#define DSIPHY_PLL_CLKBUFLR_EN 0x041c
#define DSIPHY_PLL_PLL_BANDGAP 0x0508
+#define DSIPHY_LANE_STRENGTH_CTRL_1 0x003c
+#define DSIPHY_LANE_VREG_CNTRL 0x0064
+
#define DSI_DYNAMIC_REFRESH_PLL_CTRL0 0x214
#define DSI_DYNAMIC_REFRESH_PLL_CTRL1 0x218
#define DSI_DYNAMIC_REFRESH_PLL_CTRL2 0x21C
@@ -875,6 +878,92 @@ static void mdss_dsi_8996_phy_regulator_enable(
}
+static void mdss_dsi_8996_phy_power_off(
+ struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ int ln;
+ void __iomem *base;
+
+ MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0x7f);
+
+ /* 4 lanes + clk lane configuration */
+ for (ln = 0; ln < 5; ln++) {
+ base = ctrl->phy_io.base +
+ DATALANE_OFFSET_FROM_BASE_8996;
+ base += (ln * DATALANE_SIZE_8996); /* lane base */
+
+ /* turn off phy ldo */
+ MIPI_OUTP(base + DSIPHY_LANE_VREG_CNTRL, 0x1c);
+ }
+ MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c);
+
+ /* 4 lanes + clk lane configuration */
+ for (ln = 0; ln < 5; ln++) {
+ base = ctrl->phy_io.base +
+ DATALANE_OFFSET_FROM_BASE_8996;
+ base += (ln * DATALANE_SIZE_8996); /* lane base */
+
+ MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0);
+ }
+
+ wmb(); /* make sure registers committed */
+}
+
+static void mdss_dsi_phy_power_off(
+ struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ if (ctrl->phy_power_off)
+ return;
+
+ /* supported for phy rev 2.0 */
+ if (ctrl->shared_data->phy_rev != DSI_PHY_REV_20)
+ return;
+
+ mdss_dsi_8996_phy_power_off(ctrl);
+
+ ctrl->phy_power_off = true;
+}
+
+static void mdss_dsi_8996_phy_power_on(
+ struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ int j, off, ln, cnt, ln_off;
+ void __iomem *base;
+ struct mdss_dsi_phy_ctrl *pd;
+ char *ip;
+
+ pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
+
+ /* 4 lanes + clk lane configuration */
+ for (ln = 0; ln < 5; ln++) {
+ base = ctrl->phy_io.base +
+ DATALANE_OFFSET_FROM_BASE_8996;
+ base += (ln * DATALANE_SIZE_8996); /* lane base */
+
+ /* strength, 2 * 5 */
+ cnt = 2;
+ ln_off = cnt * ln;
+ ip = &pd->strength[ln_off];
+ off = 0x38;
+ for (j = 0; j < cnt; j++, off += 4)
+ MIPI_OUTP(base + off, *ip++);
+ }
+
+ mdss_dsi_8996_phy_regulator_enable(ctrl);
+}
+
+static void mdss_dsi_phy_power_on(
+ struct mdss_dsi_ctrl_pdata *ctrl, bool mmss_clamp)
+{
+ if (mmss_clamp && (ctrl->shared_data->phy_rev != DSI_PHY_REV_20))
+ mdss_dsi_phy_init(ctrl);
+ else if ((ctrl->shared_data->phy_rev == DSI_PHY_REV_20) &&
+ ctrl->phy_power_off)
+ mdss_dsi_8996_phy_power_on(ctrl);
+
+ ctrl->phy_power_off = false;
+}
+
static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl)
{
struct mdss_dsi_phy_ctrl *pd;
@@ -1171,6 +1260,7 @@ int mdss_dsi_clk_refresh(struct mdss_panel_data *pdata, bool update_phy)
pr_err("Error in calculating phy timings\n");
return rc;
}
+ ctrl_pdata->update_phy_timing = false;
}
return rc;
@@ -1970,8 +2060,7 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
* However, when blanking the panel, we should enter ULPS
* only if ULPS during suspend feature is enabled.
*/
- if (pdata->panel_info.blank_state ==
- MDSS_PANEL_BLANK_BLANK) {
+ if (!(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT)) {
if (pdata->panel_info.ulps_suspend_enabled)
mdss_dsi_ulps_config(ctrl, 1);
} else if (mdss_dsi_ulps_feature_enabled(pdata)) {
@@ -1988,8 +2077,9 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
* Enable DSI clamps only if entering idle power collapse or
* when ULPS during suspend is enabled.
*/
- if ((pdata->panel_info.blank_state != MDSS_PANEL_BLANK_BLANK) ||
+ if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) ||
pdata->panel_info.ulps_suspend_enabled) {
+ mdss_dsi_phy_power_off(ctrl);
rc = mdss_dsi_clamp_ctrl(ctrl, 1);
if (rc)
pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
@@ -2016,23 +2106,18 @@ int mdss_dsi_post_clkon_cb(void *priv,
int rc = 0;
struct mdss_panel_data *pdata = NULL;
struct mdss_dsi_ctrl_pdata *ctrl = priv;
+ bool mmss_clamp;
pdata = &ctrl->panel_data;
if (clk & MDSS_DSI_CORE_CLK) {
- if (!pdata->panel_info.cont_splash_enabled) {
- mdss_dsi_read_hw_revision(ctrl);
- mdss_dsi_read_phy_revision(ctrl);
- }
-
+ mmss_clamp = ctrl->mmss_clamp;
/*
- * Phy and controller setup is needed if coming out of idle
+ * controller setup is needed if coming out of idle
* power collapse with clamps enabled.
*/
- if (ctrl->mmss_clamp) {
- mdss_dsi_phy_init(ctrl);
+ if (mmss_clamp)
mdss_dsi_ctrl_setup(ctrl);
- }
if (ctrl->ulps) {
/*
@@ -2064,6 +2149,13 @@ int mdss_dsi_post_clkon_cb(void *priv,
__func__, rc);
goto error;
}
+
+ /*
+ * Phy setup is needed if coming out of idle
+ * power collapse with clamps enabled.
+ */
+ if (ctrl->phy_power_off || mmss_clamp)
+ mdss_dsi_phy_power_on(ctrl, mmss_clamp);
}
if (clk & MDSS_DSI_LINK_CLK) {
if (ctrl->ulps) {
@@ -2100,9 +2192,18 @@ int mdss_dsi_post_clkoff_cb(void *priv,
pdata = &ctrl->panel_data;
for (i = DSI_MAX_PM - 1; i >= DSI_CORE_PM; i--) {
- if ((i != DSI_CORE_PM) &&
- (pdata->panel_info.blank_state !=
- MDSS_PANEL_BLANK_BLANK))
+ /*
+ * if DSI state is active
+ * 1. allow to turn off the core power module.
+ * 2. allow to turn off phy power module if it is
+ * turned off
+ *
+ * allow to turn off all power modules if DSI is not
+ * active
+ */
+ if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) &&
+ (i != DSI_CORE_PM) &&
+ (ctrl->phy_power_off && (i != DSI_PHY_PM)))
continue;
rc = msm_dss_enable_vreg(
sdata->power_data[i].vreg_config,
@@ -2140,20 +2241,22 @@ int mdss_dsi_pre_clkon_cb(void *priv,
sdata = ctrl->shared_data;
pdata = &ctrl->panel_data;
/*
- * Enable DSI core power
+ * Enable DSI core power
* 1.> PANEL_PM are controlled as part of
* panel_power_ctrl. Needed not be handled here.
* 2.> CORE_PM are controlled by dsi clk manager.
- * 2.> PHY_PM and CTRL_PM need to be enabled/disabled
+ * 3.> CTRL_PM need to be enabled/disabled
* only during unblank/blank. Their state should
* not be changed during static screen.
+ * 4.> PHY_PM can be turned enabled/disabled
+ * if phy regulators are enabled/disabled.
*/
pr_debug("%s: Enable DSI core power\n", __func__);
for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) {
- if ((i != DSI_CORE_PM) &&
- (pdata->panel_info.blank_state !=
- MDSS_PANEL_BLANK_BLANK) &&
- !pdata->panel_info.cont_splash_enabled)
+ if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) &&
+ (!pdata->panel_info.cont_splash_enabled) &&
+ (i != DSI_CORE_PM) &&
+ (ctrl->phy_power_off && (i != DSI_PHY_PM)))
continue;
rc = msm_dss_enable_vreg(
sdata->power_data[i].vreg_config,