Merge "msmfb: mdss: add mdp3 core driver"
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index de765ea..d09c03a 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -1,3 +1,6 @@
+mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o
+obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp3.o
+
mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o
mdss-mdp-objs += mdss_mdp_pp.o
mdss-mdp-objs += mdss_mdp_intf_video.o
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
new file mode 100644
index 0000000..890b00b
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -0,0 +1,917 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/memory_alloc.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+
+#include <mach/board.h>
+#include <mach/clk.h>
+#include <mach/hardware.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/iommu.h>
+#include <mach/iommu_domains.h>
+#include <mach/msm_memtypes.h>
+
+#include "mdp3.h"
+#include "mdss_fb.h"
+#include "mdp3_hwio.h"
+#include "mdp3_ctrl.h"
+
+#define MDP_CORE_HW_VERSION 0x03030304
+struct mdp3_hw_resource *mdp3_res;
+
+#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \
+ { \
+ .src = MSM_BUS_MASTER_MDP_PORT0, \
+ .dst = MSM_BUS_SLAVE_EBI_CH0, \
+ .ab = (ab_val), \
+ .ib = (ib_val), \
+ }
+
+static struct msm_bus_vectors mdp_bus_vectors[] = {
+ MDP_BUS_VECTOR_ENTRY(0, 0),
+ MDP_BUS_VECTOR_ENTRY(SZ_128M, SZ_256M),
+ MDP_BUS_VECTOR_ENTRY(SZ_256M, SZ_512M),
+};
+
+static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)];
+
+static struct msm_bus_scale_pdata mdp_bus_scale_table = {
+ .usecase = mdp_bus_usecases,
+ .num_usecases = ARRAY_SIZE(mdp_bus_usecases),
+ .name = "mdp3",
+};
+
+struct mdp3_iommu_domain_map mdp3_iommu_domains[MDP3_IOMMU_DOMAIN_MAX] = {
+ [MDP3_IOMMU_DOMAIN] = {
+ .domain_type = MDP3_IOMMU_DOMAIN,
+ .client_name = "mdp_dma",
+ .partitions = {
+ {
+ .start = SZ_128K,
+ .size = SZ_1G - SZ_128K,
+ },
+ },
+ .npartitions = 1,
+ },
+};
+
+struct mdp3_iommu_ctx_map mdp3_iommu_contexts[MDP3_IOMMU_CTX_MAX] = {
+ [MDP3_IOMMU_CTX_PPP_0] = {
+ .ctx_type = MDP3_IOMMU_CTX_PPP_0,
+ .domain = &mdp3_iommu_domains[MDP3_IOMMU_DOMAIN],
+ .ctx_name = "mdpe_0",
+ .attached = 0,
+ },
+ [MDP3_IOMMU_CTX_PPP_1] = {
+ .ctx_type = MDP3_IOMMU_CTX_PPP_1,
+ .domain = &mdp3_iommu_domains[MDP3_IOMMU_DOMAIN],
+ .ctx_name = "mdpe_1",
+ .attached = 0,
+ },
+
+ [MDP3_IOMMU_CTX_DMA_0] = {
+ .ctx_type = MDP3_IOMMU_CTX_DMA_0,
+ .domain = &mdp3_iommu_domains[MDP3_IOMMU_DOMAIN],
+ .ctx_name = "mdps_0",
+ .attached = 0,
+ },
+
+ [MDP3_IOMMU_CTX_DMA_1] = {
+ .ctx_type = MDP3_IOMMU_CTX_DMA_1,
+ .domain = &mdp3_iommu_domains[MDP3_IOMMU_DOMAIN],
+ .ctx_name = "mdps_1",
+ .attached = 0,
+ },
+};
+
+static irqreturn_t mdp3_irq_handler(int irq, void *ptr)
+{
+ int i = 0;
+ struct mdp3_hw_resource *mdata = (struct mdp3_hw_resource *)ptr;
+ u32 mdp_interrupt = MDP3_REG_READ(MDP3_REG_INTR_STATUS);
+
+ MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_interrupt);
+ pr_debug("mdp3_irq_handler irq=%d\n", mdp_interrupt);
+
+ spin_lock(&mdata->irq_lock);
+ mdp_interrupt &= mdata->irqMask;
+
+ while (mdp_interrupt && i < MDP3_MAX_INTR) {
+ if ((mdp_interrupt & 0x1) && mdata->callbacks[i].cb)
+ mdata->callbacks[i].cb(i, mdata->callbacks[i].data);
+ mdp_interrupt = mdp_interrupt >> 1;
+ i++;
+ }
+ spin_unlock(&mdata->irq_lock);
+
+ return IRQ_HANDLED;
+}
+
+void mdp3_irq_enable(int type)
+{
+ unsigned long flag;
+ int irqEnabled = 0;
+
+ pr_debug("mdp3_irq_enable type=%d\n", type);
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag);
+ if (mdp3_res->irqMask & BIT(type)) {
+ pr_debug("interrupt %d already enabled\n", type);
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+ return;
+ }
+ irqEnabled = mdp3_res->irqMask;
+ mdp3_res->irqMask |= BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
+ if (!irqEnabled)
+ enable_irq(mdp3_res->irq);
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+}
+
+void mdp3_irq_disable(int type)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag);
+ if (mdp3_res->irqMask & BIT(type)) {
+ mdp3_res->irqMask &= ~BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
+ if (!mdp3_res->irqMask)
+ disable_irq(mdp3_res->irq);
+ } else {
+ pr_debug("interrupt %d not enabled\n", type);
+ }
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+}
+
+void mdp3_irq_disable_nosync(int type)
+{
+ if (mdp3_res->irqMask & BIT(type)) {
+ mdp3_res->irqMask &= ~BIT(type);
+ MDP3_REG_WRITE(MDP3_REG_INTR_ENABLE, mdp3_res->irqMask);
+ if (!mdp3_res->irqMask)
+ disable_irq_nosync(mdp3_res->irq);
+ } else {
+ pr_debug("interrupt %d not enabled\n", type);
+ }
+}
+
+int mdp3_set_intr_callback(u32 type, struct mdp3_intr_cb *cb)
+{
+ unsigned long flag;
+
+ pr_debug("interrupt %d callback n", type);
+ spin_lock_irqsave(&mdp3_res->irq_lock, flag);
+ if (cb)
+ mdp3_res->callbacks[type] = *cb;
+ else
+ mdp3_res->callbacks[type].cb = NULL;
+
+ spin_unlock_irqrestore(&mdp3_res->irq_lock, flag);
+ return 0;
+}
+
+static int mdp3_bus_scale_register(void)
+{
+ if (!mdp3_res->bus_handle) {
+ struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table;
+ int i;
+
+ for (i = 0; i < bus_pdata->num_usecases; i++) {
+ mdp_bus_usecases[i].num_paths = 1;
+ mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i];
+ }
+
+ mdp3_res->bus_handle = msm_bus_scale_register_client(bus_pdata);
+ if (!mdp3_res->bus_handle) {
+ pr_err("not able to get bus scale\n");
+ return -ENOMEM;
+ }
+ pr_debug("register bus_hdl=%x\n", mdp3_res->bus_handle);
+ }
+ return 0;
+}
+
+static void mdp3_bus_scale_unregister(void)
+{
+ pr_debug("unregister bus_handle=%x\n", mdp3_res->bus_handle);
+
+ if (mdp3_res->bus_handle)
+ msm_bus_scale_unregister_client(mdp3_res->bus_handle);
+}
+
+int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota)
+{
+ static int current_bus_idx;
+ int bus_idx;
+ int rc;
+
+ if (mdp3_res->bus_handle < 1) {
+ pr_err("invalid bus handle %d\n", mdp3_res->bus_handle);
+ return -EINVAL;
+ }
+
+ if ((ab_quota | ib_quota) == 0) {
+ bus_idx = 0;
+ } else {
+ int num_cases = mdp_bus_scale_table.num_usecases;
+ struct msm_bus_vectors *vect = NULL;
+
+ bus_idx = (current_bus_idx % (num_cases - 1)) + 1;
+
+ /* aligning to avoid performing updates for small changes */
+ ab_quota = ALIGN(ab_quota, SZ_64M);
+ ib_quota = ALIGN(ib_quota, SZ_64M);
+
+ vect = mdp_bus_scale_table.usecase[current_bus_idx].vectors;
+ if ((ab_quota == vect->ab) && (ib_quota == vect->ib)) {
+ pr_debug("skip bus scaling, no change in vectors\n");
+ return 0;
+ }
+
+ vect = mdp_bus_scale_table.usecase[bus_idx].vectors;
+ vect->ab = ab_quota;
+ vect->ib = ib_quota;
+
+ pr_debug("bus scale idx=%d ab=%llu ib=%llu\n", bus_idx,
+ vect->ab, vect->ib);
+ }
+ current_bus_idx = bus_idx;
+ rc = msm_bus_scale_client_update_request(mdp3_res->bus_handle, bus_idx);
+ return rc;
+}
+
+static int mdp3_clk_update(u32 clk_idx, u32 enable)
+{
+ int ret = -EINVAL;
+ struct clk *clk;
+ int count = 0;
+
+ if (clk_idx >= MDP3_MAX_CLK || !mdp3_res->clocks[clk_idx])
+ return -ENODEV;
+
+ clk = mdp3_res->clocks[clk_idx];
+
+ if (enable)
+ mdp3_res->clock_ref_count[clk_idx]++;
+ else
+ mdp3_res->clock_ref_count[clk_idx]--;
+
+ count = mdp3_res->clock_ref_count[clk_idx];
+ if (count == 1) {
+ pr_debug("clk=%d en=%d\n", clk_idx, enable);
+ ret = clk_prepare_enable(clk);
+ } else if (count == 0) {
+ pr_debug("clk=%d disable\n", clk_idx);
+ clk_disable_unprepare(clk);
+ ret = 0;
+ } else if (count < 0) {
+ pr_err("clk=%d count=%d\n", clk_idx, count);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+int mdp3_vsync_clk_enable(int enable)
+{
+ int ret = 0;
+
+ pr_debug("vsync clk enable=%d\n", enable);
+ mutex_lock(&mdp3_res->res_mutex);
+ mdp3_clk_update(MDP3_CLK_VSYNC, enable);
+ mutex_unlock(&mdp3_res->res_mutex);
+ return ret;
+}
+
+int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate)
+{
+ int ret = 0;
+ unsigned long rounded_rate;
+ struct clk *clk = mdp3_res->clocks[clk_type];
+
+ if (clk) {
+ mutex_lock(&mdp3_res->res_mutex);
+ rounded_rate = clk_round_rate(clk, clk_rate);
+ if (IS_ERR_VALUE(rounded_rate)) {
+ pr_err("unable to round rate err=%ld\n", rounded_rate);
+ mutex_unlock(&mdp3_res->res_mutex);
+ return -EINVAL;
+ }
+ if (rounded_rate != clk_get_rate(clk)) {
+ ret = clk_set_rate(clk, rounded_rate);
+ if (ret)
+ pr_err("clk_set_rate failed ret=%d\n", ret);
+ else
+ pr_debug("mdp clk rate=%lu\n", rounded_rate);
+ }
+ mutex_unlock(&mdp3_res->res_mutex);
+ } else {
+ pr_err("mdp src clk not setup properly\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+unsigned long mdp3_get_clk_rate(u32 clk_idx)
+{
+ unsigned long clk_rate = 0;
+ struct clk *clk;
+
+ if (clk_idx >= MDP3_MAX_CLK)
+ return -ENODEV;
+
+ clk = mdp3_res->clocks[clk_idx];
+
+ if (clk) {
+ mutex_lock(&mdp3_res->res_mutex);
+ clk_rate = clk_get_rate(clk);
+ mutex_unlock(&mdp3_res->res_mutex);
+ }
+ return clk_rate;
+}
+
+static int mdp3_clk_register(char *clk_name, int clk_idx)
+{
+ struct clk *tmp;
+
+ if (clk_idx >= MDP3_MAX_CLK) {
+ pr_err("invalid clk index %d\n", clk_idx);
+ return -EINVAL;
+ }
+
+ tmp = devm_clk_get(&mdp3_res->pdev->dev, clk_name);
+ if (IS_ERR(tmp)) {
+ pr_err("unable to get clk: %s\n", clk_name);
+ return PTR_ERR(tmp);
+ }
+
+ mdp3_res->clocks[clk_idx] = tmp;
+
+ return 0;
+}
+
+static int mdp3_clk_setup(void)
+{
+ int rc;
+
+ rc = mdp3_clk_register("iface_clk", MDP3_CLK_AHB);
+ if (rc)
+ return rc;
+
+ rc = mdp3_clk_register("core_clk", MDP3_CLK_CORE);
+ if (rc)
+ return rc;
+
+ rc = mdp3_clk_register("vsync_clk", MDP3_CLK_VSYNC);
+ if (rc)
+ return rc;
+
+ rc = mdp3_clk_register("lcdc_clk", MDP3_CLK_LCDC);
+ if (rc)
+ return rc;
+
+ return rc;
+}
+
+static void mdp3_clk_remove(void)
+{
+ clk_put(mdp3_res->clocks[MDP3_CLK_AHB]);
+ clk_put(mdp3_res->clocks[MDP3_CLK_CORE]);
+ clk_put(mdp3_res->clocks[MDP3_CLK_VSYNC]);
+ clk_put(mdp3_res->clocks[MDP3_CLK_LCDC]);
+}
+
+int mdp3_clk_enable(int enable)
+{
+ int rc;
+
+ pr_debug("MDP CLKS %s\n", (enable ? "Enable" : "Disable"));
+
+ mutex_lock(&mdp3_res->res_mutex);
+ rc = mdp3_clk_update(MDP3_CLK_AHB, enable);
+ rc |= mdp3_clk_update(MDP3_CLK_CORE, enable);
+ rc |= mdp3_clk_update(MDP3_CLK_VSYNC, enable);
+ mutex_unlock(&mdp3_res->res_mutex);
+ return rc;
+}
+
+static int mdp3_irq_setup(void)
+{
+ int ret;
+
+ ret = devm_request_irq(&mdp3_res->pdev->dev,
+ mdp3_res->irq,
+ mdp3_irq_handler,
+ IRQF_DISABLED, "MDP", mdp3_res);
+ if (ret) {
+ pr_err("mdp request_irq() failed!\n");
+ return ret;
+ }
+ disable_irq(mdp3_res->irq);
+ return 0;
+}
+
+static int mdp3_iommu_fault_handler(struct iommu_domain *domain,
+ struct device *dev, unsigned long iova, int flags, void *token)
+{
+ pr_err("MDP IOMMU page fault: iova 0x%lx\n", iova);
+ return 0;
+}
+
+int mdp3_iommu_attach(int context)
+{
+ struct mdp3_iommu_ctx_map *context_map;
+ struct mdp3_iommu_domain_map *domain_map;
+
+ if (context >= MDP3_IOMMU_CTX_MAX)
+ return -EINVAL;
+
+ context_map = mdp3_res->iommu_contexts + context;
+ if (context_map->attached) {
+ pr_warn("mdp iommu already attached\n");
+ return 0;
+ }
+
+ domain_map = context_map->domain;
+
+ iommu_attach_device(domain_map->domain, context_map->ctx);
+
+ context_map->attached = true;
+ return 0;
+}
+
+int mdp3_iommu_dettach(int context)
+{
+ struct mdp3_iommu_ctx_map *context_map;
+ struct mdp3_iommu_domain_map *domain_map;
+
+ if (context >= MDP3_IOMMU_CTX_MAX)
+ return -EINVAL;
+
+ context_map = mdp3_res->iommu_contexts + context;
+ if (!context_map->attached) {
+ pr_warn("mdp iommu not attached\n");
+ return 0;
+ }
+
+ domain_map = context_map->domain;
+ iommu_detach_device(domain_map->domain, context_map->ctx);
+ context_map->attached = false;
+
+ return 0;
+}
+
+int mdp3_iommu_domain_init(void)
+{
+ struct msm_iova_layout layout;
+ int i;
+
+ if (mdp3_res->domains) {
+ pr_warn("iommu domain already initialized\n");
+ return 0;
+ }
+
+ for (i = 0; i < MDP3_IOMMU_DOMAIN_MAX; i++) {
+ int domain_idx;
+ 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;
+
+ domain_idx = msm_register_domain(&layout);
+ if (IS_ERR_VALUE(domain_idx))
+ return -EINVAL;
+
+ mdp3_iommu_domains[i].domain_idx = domain_idx;
+ mdp3_iommu_domains[i].domain = msm_get_iommu_domain(domain_idx);
+ if (!mdp3_iommu_domains[i].domain) {
+ pr_err("unable to get iommu domain(%d)\n",
+ domain_idx);
+ return -EINVAL;
+ }
+ iommu_set_fault_handler(mdp3_iommu_domains[i].domain,
+ mdp3_iommu_fault_handler,
+ NULL);
+ }
+
+ mdp3_res->domains = mdp3_iommu_domains;
+
+ return 0;
+}
+
+int mdp3_iommu_context_init(void)
+{
+ int i;
+
+ if (mdp3_res->iommu_contexts) {
+ pr_warn("iommu context already initialized\n");
+ return 0;
+ }
+
+ for (i = 0; i < MDP3_IOMMU_CTX_MAX; i++) {
+ mdp3_iommu_contexts[i].ctx =
+ msm_iommu_get_ctx(mdp3_iommu_contexts[i].ctx_name);
+
+ if (!mdp3_iommu_contexts[i].ctx) {
+ pr_warn("unable to get iommu ctx(%s)\n",
+ mdp3_iommu_contexts[i].ctx_name);
+ return -EINVAL;
+ }
+ }
+
+ mdp3_res->iommu_contexts = mdp3_iommu_contexts;
+
+ return 0;
+}
+
+int mdp3_iommu_init(void)
+{
+ int ret;
+
+ ret = mdp3_iommu_domain_init();
+ if (ret) {
+ pr_err("mdp3 iommu domain init fails\n");
+ return ret;
+ }
+
+ ret = mdp3_iommu_context_init();
+ if (ret) {
+ pr_err("mdp3 iommu context init fails\n");
+ return ret;
+ }
+ return ret;
+}
+
+static int mdp3_check_version(void)
+{
+ int rc;
+
+ rc = mdp3_clk_update(MDP3_CLK_AHB, 1);
+ if (rc)
+ return rc;
+
+ mdp3_res->mdp_rev = MDP3_REG_READ(MDP3_REG_HW_VERSION);
+
+ rc = mdp3_clk_update(MDP3_CLK_AHB, 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;
+ }
+ return rc;
+}
+
+static int mdp3_hw_init(void)
+{
+ int i;
+
+ for (i = MDP3_DMA_P; i < MDP3_DMA_MAX; i++) {
+ mdp3_res->dma[i].dma_sel = i;
+ 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[MDP3_DMA_S].capability = MDP3_DMA_CAP_DITHER;
+ mdp3_res->dma[MDP3_DMA_E].available = 0;
+
+ for (i = MDP3_DMA_OUTPUT_SEL_AHB; i < MDP3_DMA_OUTPUT_SEL_MAX; i++) {
+ mdp3_res->intf[i].cfg.type = i;
+ mdp3_res->intf[i].active = 0;
+ mdp3_res->intf[i].in_use = 0;
+ mdp3_res->intf[i].available = 1;
+ }
+ mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_AHB].available = 0;
+ mdp3_res->intf[MDP3_DMA_OUTPUT_SEL_LCDC].available = 0;
+
+ return 0;
+}
+
+static int mdp3_res_init(void)
+{
+ int rc = 0;
+
+ rc = mdp3_irq_setup();
+ if (rc)
+ return rc;
+
+ rc = mdp3_clk_setup();
+ if (rc)
+ return rc;
+
+ mdp3_res->ion_client = msm_ion_client_create(-1, mdp3_res->pdev->name);
+ if (IS_ERR_OR_NULL(mdp3_res->ion_client)) {
+ pr_err("msm_ion_client_create() return error (%p)\n",
+ mdp3_res->ion_client);
+ mdp3_res->ion_client = NULL;
+ return -EINVAL;
+ }
+
+ rc = mdp3_iommu_init();
+ if (rc)
+ return rc;
+
+ rc = mdp3_iommu_attach(MDP3_IOMMU_CTX_DMA_0);
+ if (rc) {
+ pr_err("fail to attach DMA-P context 0\n");
+ return rc;
+ }
+ rc = mdp3_bus_scale_register();
+ if (rc) {
+ pr_err("unable to register bus scaling\n");
+ return rc;
+ }
+
+ rc = mdp3_hw_init();
+
+ return rc;
+}
+
+static int mdp3_parse_dt(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdp_phys");
+ if (!res) {
+ pr_err("unable to get MDP base address\n");
+ return -EINVAL;
+ }
+
+ mdp3_res->mdp_reg_size = resource_size(res);
+ mdp3_res->mdp_base = devm_ioremap(&pdev->dev, res->start,
+ mdp3_res->mdp_reg_size);
+ if (unlikely(!mdp3_res->mdp_base)) {
+ pr_err("unable to map MDP base\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("MDP HW Base phy_Address=0x%x virt=0x%x\n",
+ (int) res->start,
+ (int) mdp3_res->mdp_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ pr_err("unable to get MDSS irq\n");
+ return -EINVAL;
+ }
+ mdp3_res->irq = res->start;
+
+ return 0;
+}
+
+static int mdp3_init(struct msm_fb_data_type *mfd)
+{
+ return mdp3_ctrl_init(mfd);
+}
+
+u32 mdp3_fb_stride(u32 fb_index, u32 xres, int bpp)
+{
+ /*
+ * The adreno GPU hardware requires that the pitch be aligned to
+ * 32 pixels for color buffers, so for the cases where the GPU
+ * is writing directly to fb0, the framebuffer pitch
+ * also needs to be 32 pixel aligned
+ */
+
+ if (fb_index == 0)
+ return ALIGN(xres, 32) * bpp;
+ else
+ return xres * bpp;
+}
+
+/*
+ * physical contiguous memory should be allocated in mdss_fb, and SMMU
+ * virtual address mapping can be done in the MDP h/w specific code. It
+ * should have a reference count, if none is current mapped, the SMMU context
+ * can bedetached, thus allowing power saving in SMMU.
+ */
+static int mdp3_fbmem_alloc(struct msm_fb_data_type *mfd)
+{
+ int dom;
+ void *virt = NULL;
+ unsigned long phys = 0;
+ size_t size;
+ u32 yres = mfd->fbi->var.yres_virtual;
+
+ size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres);
+
+ if (mfd->index == 0) {
+ virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
+ if (!virt) {
+ pr_err("unable to alloc fbmem size=%u\n", size);
+ return -ENOMEM;
+ }
+ phys = memory_pool_node_paddr(virt);
+ dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
+ msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0,
+ &mfd->iova);
+
+ pr_debug("allocating %u bytes at %p (%lx phys) for fb %d\n",
+ size, virt, phys, mfd->index);
+ } else {
+ size = 0;
+ }
+
+ mfd->fbi->screen_base = virt;
+ mfd->fbi->fix.smem_start = phys;
+ mfd->fbi->fix.smem_len = size;
+ return 0;
+}
+
+struct mdp3_dma *mdp3_get_dma_pipe(int capability)
+{
+ int i;
+
+ for (i = MDP3_DMA_P; i < MDP3_DMA_MAX; i++) {
+ if (!mdp3_res->dma[i].in_use && mdp3_res->dma[i].available &&
+ mdp3_res->dma[i].capability & capability) {
+ mdp3_res->dma[i].in_use = true;
+ return &mdp3_res->dma[i];
+ }
+ }
+ return NULL;
+}
+
+struct mdp3_intf *mdp3_get_display_intf(int type)
+{
+ int i;
+
+ for (i = MDP3_DMA_OUTPUT_SEL_AHB; i < MDP3_DMA_OUTPUT_SEL_MAX; i++) {
+ if (!mdp3_res->intf[i].in_use && mdp3_res->intf[i].available &&
+ mdp3_res->intf[i].cfg.type == type) {
+ mdp3_res->intf[i].in_use = true;
+ return &mdp3_res->intf[i];
+ }
+ }
+ return NULL;
+}
+
+static int mdp3_probe(struct platform_device *pdev)
+{
+ int rc;
+ static struct msm_mdp_interface mdp3_interface = {
+ .init_fnc = mdp3_init,
+ .fb_mem_alloc_fnc = mdp3_fbmem_alloc,
+ .fb_stride = mdp3_fb_stride,
+ };
+
+ if (!pdev->dev.of_node) {
+ pr_err("MDP driver only supports device tree probe\n");
+ return -ENOTSUPP;
+ }
+
+ if (mdp3_res) {
+ pr_err("MDP already initialized\n");
+ return -EINVAL;
+ }
+
+ mdp3_res = devm_kzalloc(&pdev->dev, sizeof(struct mdp3_hw_resource),
+ GFP_KERNEL);
+ if (mdp3_res == NULL)
+ return -ENOMEM;
+
+ pdev->id = 0;
+ mdp3_res->pdev = pdev;
+ mutex_init(&mdp3_res->res_mutex);
+ spin_lock_init(&mdp3_res->irq_lock);
+ platform_set_drvdata(pdev, mdp3_res);
+
+ rc = mdp3_parse_dt(pdev);
+ if (rc)
+ goto probe_done;
+
+ rc = mdp3_res_init();
+ if (rc) {
+ pr_err("unable to initialize mdp3 resources\n");
+ goto probe_done;
+ }
+
+ rc = mdp3_check_version();
+ if (rc) {
+ pr_err("mdp3 check version failed\n");
+ goto probe_done;
+ }
+
+ rc = mdss_fb_register_mdp_instance(&mdp3_interface);
+ if (rc)
+ pr_err("unable to register mdp instance\n");
+
+probe_done:
+ if (IS_ERR_VALUE(rc)) {
+ devm_kfree(&pdev->dev, mdp3_res);
+ mdp3_res = NULL;
+ }
+
+ return rc;
+}
+
+static int mdp3_suspend_sub(struct mdp3_hw_resource *mdata)
+{
+ return 0;
+}
+
+static int mdp3_resume_sub(struct mdp3_hw_resource *mdata)
+{
+ return 0;
+}
+
+static int mdp3_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev);
+
+ if (!mdata)
+ return -ENODEV;
+
+ pr_debug("display suspend\n");
+
+ return mdp3_suspend_sub(mdata);
+}
+
+static int mdp3_resume(struct platform_device *pdev)
+{
+ struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev);
+
+ if (!mdata)
+ return -ENODEV;
+
+ pr_debug("display resume\n");
+
+ return mdp3_resume_sub(mdata);
+}
+
+static int mdp3_remove(struct platform_device *pdev)
+{
+ struct mdp3_hw_resource *mdata = platform_get_drvdata(pdev);
+
+ if (!mdata)
+ return -ENODEV;
+ pm_runtime_disable(&pdev->dev);
+ mdp3_bus_scale_unregister();
+ mdp3_clk_remove();
+ return 0;
+}
+
+static const struct of_device_id mdp3_dt_match[] = {
+ { .compatible = "qcom,mdss_mdp3",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mdp3_dt_match);
+EXPORT_COMPAT("qcom,mdss_mdp3");
+
+static struct platform_driver mdp3_driver = {
+ .probe = mdp3_probe,
+ .remove = mdp3_remove,
+ .suspend = mdp3_suspend,
+ .resume = mdp3_resume,
+ .shutdown = NULL,
+ .driver = {
+ .name = "mdp3",
+ .of_match_table = mdp3_dt_match,
+ },
+};
+
+static int __init mdp3_driver_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mdp3_driver);
+ if (ret) {
+ pr_err("register mdp3 driver failed!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+module_init(mdp3_driver_init);
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
new file mode 100644
index 0000000..c853664
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3.h
@@ -0,0 +1,123 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2007 Google Incorporated
+ *
+ * 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 MDP3_H
+#define MDP3_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/earlysuspend.h>
+
+#include <mach/iommu_domains.h>
+
+#include "mdp3_dma.h"
+
+enum {
+ MDP3_CLK_AHB,
+ MDP3_CLK_CORE,
+ MDP3_CLK_VSYNC,
+ MDP3_CLK_LCDC,
+ MDP3_MAX_CLK
+};
+
+enum {
+ MDP3_IOMMU_DOMAIN,
+ MDP3_IOMMU_DOMAIN_MAX
+};
+
+enum {
+ MDP3_IOMMU_CTX_PPP_0,
+ MDP3_IOMMU_CTX_PPP_1,
+ MDP3_IOMMU_CTX_DMA_0,
+ MDP3_IOMMU_CTX_DMA_1,
+ MDP3_IOMMU_CTX_MAX
+};
+
+enum {
+ MDP3_BW_CLIENT_DMA_P,
+ MDP3_BW_CLIENT_DMA_S,
+ MDP3_BW_CLIENT_DMA_E,
+ MDP3_BW_CLIENT_PPP,
+};
+
+struct mdp3_iommu_domain_map {
+ u32 domain_type;
+ char *client_name;
+ struct msm_iova_partition partitions[1];
+ int npartitions;
+ int domain_idx;
+ struct iommu_domain *domain;
+};
+
+struct mdp3_iommu_ctx_map {
+ u32 ctx_type;
+ struct mdp3_iommu_domain_map *domain;
+ char *ctx_name;
+ struct device *ctx;
+ int attached;
+};
+
+#define MDP3_MAX_INTR 28
+
+struct mdp3_intr_cb {
+ void (*cb)(int type, void *);
+ void *data;
+};
+
+struct mdp3_hw_resource {
+ struct platform_device *pdev;
+ u32 mdp_rev;
+
+ struct mutex res_mutex;
+
+ struct clk *clocks[MDP3_MAX_CLK];
+ int clock_ref_count[MDP3_MAX_CLK];
+
+ char __iomem *mdp_base;
+ size_t mdp_reg_size;
+
+ u32 irq;
+ u32 bus_handle;
+
+ struct ion_client *ion_client;
+ struct mdp3_iommu_domain_map *domains;
+ struct mdp3_iommu_ctx_map *iommu_contexts;
+
+ struct mdp3_dma dma[MDP3_DMA_MAX];
+ struct mdp3_intf intf[MDP3_DMA_OUTPUT_SEL_MAX];
+
+ spinlock_t irq_lock;
+ u32 irqMask;
+ struct mdp3_intr_cb callbacks[MDP3_MAX_INTR];
+
+ struct early_suspend suspend_handler;
+};
+
+extern struct mdp3_hw_resource *mdp3_res;
+
+struct mdp3_dma *mdp3_get_dma_pipe(int capability);
+struct mdp3_intf *mdp3_get_display_intf(int type);
+void mdp3_irq_enable(int type);
+void mdp3_irq_disable(int type);
+void mdp3_irq_disable_nosync(int type);
+int mdp3_set_intr_callback(u32 type, struct mdp3_intr_cb *cb);
+int mdp3_clk_set_rate(int clk_type, unsigned long clk_rate);
+int mdp3_clk_enable(int enable);
+int mdp3_bus_scale_set_quota(int client, u64 ab_quota, u64 ib_quota);
+
+#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)
+
+#endif /* MDP3_H */
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
new file mode 100644
index 0000000..e07c0a4
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -0,0 +1,511 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include "mdp3_ctrl.h"
+#include "mdp3.h"
+
+#define MDP_VSYNC_CLK_RATE 19200000
+#define VSYNC_PERIOD 16
+
+void vsync_notify_handler(void *arg)
+{
+ struct mdp3_session_data *session = (struct mdp3_session_data *)session;
+ complete(&session->vsync_comp);
+}
+
+static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable)
+{
+ struct mdp3_session_data *mdp3_session;
+ struct mdp3_vsync_notification vsync_client;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
+ !mdp3_session->intf)
+ return -ENODEV;
+
+ vsync_client.handler = vsync_notify_handler;
+ vsync_client.arg = mdp3_session;
+
+ mutex_lock(&mdp3_session->lock);
+ if (!mdp3_session->status) {
+ pr_debug("fb%d is not on yet", mfd->index);
+ mutex_unlock(&mdp3_session->lock);
+ return -EINVAL;
+ }
+
+ mdp3_session->dma->vsync_enable(mdp3_session->dma, &vsync_client);
+ mutex_unlock(&mdp3_session->lock);
+ return 0;
+}
+
+static ssize_t mdp3_vsync_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 mdp3_session_data *mdp3_session = NULL;
+ u64 vsync_ticks;
+ ktime_t vsync_time;
+ int rc;
+
+ if (!mfd || !mfd->mdp.private1)
+ return 0;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+
+ rc = wait_for_completion_interruptible_timeout(
+ &mdp3_session->vsync_comp,
+ msecs_to_jiffies(VSYNC_PERIOD * 5));
+ if (rc <= 0) {
+ pr_warn("vsync wait on fb%d interrupted (%d)\n",
+ mfd->index, rc);
+ return -EBUSY;
+ }
+
+ vsync_time = mdp3_session->dma->get_vsync_time(mdp3_session->dma);
+ vsync_ticks = ktime_to_ns(vsync_time);
+
+ pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
+ rc = snprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks);
+ return rc;
+}
+
+static DEVICE_ATTR(vsync_event, S_IRUGO, mdp3_vsync_show_event, NULL);
+
+static struct attribute *vsync_fs_attrs[] = {
+ &dev_attr_vsync_event.attr,
+ NULL,
+};
+
+static struct attribute_group vsync_fs_attr_group = {
+ .attrs = vsync_fs_attrs,
+};
+
+static int mdp3_ctrl_res_req_dma(struct msm_fb_data_type *mfd, int status)
+{
+ int rc = 0;
+ if (status) {
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+ int ab = 0;
+ int ib = 0;
+ unsigned long core_clk = 0;
+ int vtotal = 0;
+ ab = panel_info->xres * panel_info->yres * 4;
+ ab *= panel_info->mipi.frame_rate;
+ ib = (ab * 3) / 2;
+ vtotal = panel_info->lcdc.v_back_porch +
+ panel_info->lcdc.v_front_porch +
+ panel_info->lcdc.v_pulse_width +
+ panel_info->yres;
+ core_clk = panel_info->xres * panel_info->yres;
+ core_clk *= panel_info->mipi.frame_rate;
+ core_clk = (core_clk / panel_info->yres) * vtotal;
+ mdp3_clk_set_rate(MDP3_CLK_CORE, core_clk);
+ mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE);
+
+ rc = mdp3_clk_enable(true);
+ if (rc)
+ return rc;
+
+ mdp3_bus_scale_set_quota(MDP3_BW_CLIENT_DMA_P, ab, ib);
+ } else {
+ rc = mdp3_clk_enable(false);
+ rc |= mdp3_bus_scale_set_quota(MDP3_BW_CLIENT_DMA_P, 0, 0);
+ }
+ return rc;
+}
+
+static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd)
+{
+ int type;
+ switch (mfd->panel.type) {
+ case MIPI_VIDEO_PANEL:
+ type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
+ break;
+ case MIPI_CMD_PANEL:
+ type = MDP3_DMA_OUTPUT_SEL_DSI_CMD;
+ break;
+ case LCDC_PANEL:
+ type = MDP3_DMA_OUTPUT_SEL_LCDC;
+ break;
+ default:
+ type = MDP3_DMA_OUTPUT_SEL_MAX;
+ }
+ return type;
+}
+
+static int mdp3_ctrl_get_source_format(struct msm_fb_data_type *mfd)
+{
+ int format;
+ switch (mfd->fb_imgType) {
+ case MDP_RGB_565:
+ format = MDP3_DMA_IBUF_FORMAT_RGB565;
+ break;
+ case MDP_RGB_888:
+ format = MDP3_DMA_IBUF_FORMAT_RGB888;
+ break;
+ case MDP_ARGB_8888:
+ case MDP_RGBA_8888:
+ format = MDP3_DMA_IBUF_FORMAT_XRGB8888;
+ break;
+ default:
+ format = MDP3_DMA_IBUF_FORMAT_UNDEFINED;
+ }
+ return format;
+}
+
+static int mdp3_ctrl_get_pack_pattern(struct msm_fb_data_type *mfd)
+{
+ int packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_RGB;
+ if (mfd->fb_imgType == MDP_RGBA_8888)
+ packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_BGR;
+ return packPattern;
+}
+
+static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd,
+ struct mdp3_intf *intf)
+{
+ int rc;
+ struct mdp3_intf_cfg cfg;
+ struct mdp3_video_intf_cfg *video = &cfg.video;
+ struct mdss_panel_info *p = mfd->panel_info;
+ int h_back_porch = p->lcdc.h_back_porch;
+ int h_front_porch = p->lcdc.h_front_porch;
+ int w = p->xres;
+ int v_back_porch = p->lcdc.v_back_porch;
+ int v_front_porch = p->lcdc.v_front_porch;
+ int h = p->yres;
+ int h_sync_skew = p->lcdc.hsync_skew;
+ int h_pulse_width = p->lcdc.h_pulse_width;
+ 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;
+ vsync_period *= hsync_period;
+
+ cfg.type = mdp3_ctrl_get_intf_type(mfd);
+ if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ video->hsync_period = hsync_period;
+ video->hsync_pulse_width = h_pulse_width;
+ video->vsync_period = vsync_period;
+ video->vsync_pulse_width = v_pulse_width * hsync_period;
+ video->display_start_x = h_back_porch + h_pulse_width;
+ video->display_end_x = hsync_period - h_front_porch - 1;
+ video->display_start_y =
+ (v_back_porch + v_pulse_width) * hsync_period;
+ video->display_end_y =
+ vsync_period - v_front_porch * hsync_period - 1;
+ video->active_start_x = video->display_start_x;
+ video->active_end_x = video->display_end_x;
+ video->active_h_enable = true;
+ video->active_start_y = video->display_start_y;
+ video->active_end_y = video->display_end_y;
+ video->active_v_enable = true;
+ video->hsync_skew = h_sync_skew;
+ video->hsync_polarity = 1;
+ video->vsync_polarity = 1;
+ video->de_polarity = 1;
+ } else if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ cfg.dsi_cmd.primary_dsi_cmd_id = 0;
+ cfg.dsi_cmd.secondary_dsi_cmd_id = 1;
+ cfg.dsi_cmd.dsi_cmd_tg_intf_sel = 0;
+ } else
+ return -EINVAL;
+ rc = mdp3_intf_init(intf, &cfg);
+ return rc;
+}
+
+static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
+ struct mdp3_dma *dma)
+{
+ int rc;
+ struct mdss_panel_info *panel_info = mfd->panel_info;
+ struct fb_info *fbi = mfd->fbi;
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ struct mdp3_dma_output_config outputConfig;
+ struct mdp3_dma_source sourceConfig;
+
+ fix = &fbi->fix;
+ var = &fbi->var;
+
+ sourceConfig.format = mdp3_ctrl_get_source_format(mfd);
+ sourceConfig.width = panel_info->xres;
+ sourceConfig.height = panel_info->yres;
+ sourceConfig.x = 0;
+ sourceConfig.y = 0;
+ sourceConfig.stride = fix->line_length;
+ sourceConfig.buf = (void *)mfd->iova;
+
+ outputConfig.dither_en = 0;
+ outputConfig.out_sel = mdp3_ctrl_get_intf_type(mfd);
+ outputConfig.bit_mask_polarity = 0;
+ outputConfig.color_components_flip = 0;
+ outputConfig.pack_pattern = mdp3_ctrl_get_pack_pattern(mfd);
+ outputConfig.pack_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;
+
+ rc = mdp3_dma_init(dma, &sourceConfig, &outputConfig);
+ return rc;
+}
+
+static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session;
+ struct mdss_panel_data *panel;
+
+ pr_debug("mdp3_ctrl_on\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_on no device");
+ return -ENODEV;
+ }
+ mutex_lock(&mdp3_session->lock);
+ if (mdp3_session->status) {
+ pr_info("fb%d is on already", mfd->index);
+ goto on_error;
+ }
+
+ rc = mdp3_ctrl_res_req_dma(mfd, 1);
+ if (rc) {
+ pr_err("resource request for dma on failed\n");
+ goto on_error;
+ }
+
+ rc = mdp3_ctrl_dma_init(mfd, mdp3_session->dma);
+ if (rc) {
+ pr_err("dma init failed\n");
+ goto on_error;
+ }
+
+ rc = mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
+ if (rc) {
+ pr_err("display interface init failed\n");
+ goto on_error;
+ }
+
+ panel = mdp3_session->panel;
+
+ if (panel->event_handler)
+ rc = panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
+
+ if (rc) {
+ pr_err("fail to turn on the panel\n");
+ goto on_error;
+ }
+
+ rc = mdp3_session->dma->start(mdp3_session->dma, mdp3_session->intf);
+ if (rc) {
+ pr_err("fail to start the MDP display interface\n");
+ goto on_error;
+ }
+
+on_error:
+ if (!rc)
+ mdp3_session->status = 1;
+
+ mutex_unlock(&mdp3_session->lock);
+ return rc;
+}
+
+static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session;
+ struct mdss_panel_data *panel;
+
+ pr_debug("mdp3_ctrl_off\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_on no device");
+ return -ENODEV;
+ }
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (!mdp3_session->status) {
+ pr_info("fb%d is off already", mfd->index);
+ goto off_error;
+ }
+
+ panel = mdp3_session->panel;
+ if (panel->event_handler)
+ rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL);
+
+ if (rc)
+ pr_err("fail to turn off the panel\n");
+
+ rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
+
+ if (rc)
+ pr_err("fail to stop the MDP3 dma\n");
+
+ rc = mdp3_ctrl_res_req_dma(mfd, 0);
+ if (rc)
+ pr_err("resource release for dma on failed\n");
+
+off_error:
+ mdp3_session->status = 0;
+
+ mutex_unlock(&mdp3_session->lock);
+ return 0;
+}
+
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
+{
+ struct fb_info *fbi;
+ struct mdp3_session_data *mdp3_session;
+ u32 offset;
+ int bpp;
+
+ pr_debug("mdp3_ctrl_pan_display\n");
+ if (!mfd || !mfd->mdp.private1)
+ return;
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ if (!mdp3_session || !mdp3_session->dma)
+ return;
+
+ if (!mdp3_session->status) {
+ pr_err("mdp3_ctrl_pan_display, display off!\n");
+ return;
+ }
+
+ mutex_lock(&mdp3_session->lock);
+ fbi = mfd->fbi;
+
+ bpp = fbi->var.bits_per_pixel / 8;
+ offset = fbi->var.xoffset * bpp +
+ fbi->var.yoffset * fbi->fix.line_length;
+
+ if (offset > fbi->fix.smem_len) {
+ pr_err("invalid fb offset=%u total length=%u\n",
+ offset, fbi->fix.smem_len);
+ goto pan_error;
+ }
+
+ mdp3_session->dma->update(mdp3_session->dma,
+ (void *)mfd->iova + offset);
+pan_error:
+ mutex_unlock(&mdp3_session->lock);
+}
+
+static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
+ u32 cmd, void __user *argp)
+{
+ int rc = -EINVAL;
+ struct mdp3_session_data *mdp3_session;
+ int val;
+
+ pr_debug("mdp3_ctrl_ioctl_handler\n");
+
+ mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
+ if (!mdp3_session)
+ return -ENODEV;
+
+ if (!mdp3_session->status) {
+ pr_err("mdp3_ctrl_ioctl_handler, display off!\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case MSMFB_VSYNC_CTRL:
+ case MSMFB_OVERLAY_VSYNC_CTRL:
+ if (!copy_from_user(&val, argp, sizeof(val))) {
+ rc = mdp3_ctrl_vsync_enable(mfd, val);
+ if (!val)
+ init_completion(&mdp3_session->vsync_comp);
+ } else {
+ pr_err("MSMFB_OVERLAY_VSYNC_CTRL failed\n");
+ rc = -EFAULT;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
+{
+ struct device *dev = mfd->fbi->dev;
+ struct msm_mdp_interface *mdp3_interface = &mfd->mdp;
+ struct mdp3_session_data *mdp3_session = NULL;
+ u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
+ int rc;
+
+ pr_debug("mdp3_ctrl_init\n");
+ mdp3_interface->on_fnc = mdp3_ctrl_on;
+ mdp3_interface->off_fnc = mdp3_ctrl_off;
+ mdp3_interface->do_histogram = NULL;
+ mdp3_interface->cursor_update = NULL;
+ mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
+ mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
+ mdp3_interface->kickoff_fnc = NULL;
+
+ mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
+ if (!mdp3_session) {
+ pr_err("fail to allocate mdp3 private data structure");
+ return -ENOMEM;
+ }
+ memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
+ mutex_init(&mdp3_session->lock);
+ init_completion(&mdp3_session->vsync_comp);
+ mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
+ if (!mdp3_session->dma) {
+ rc = -ENODEV;
+ goto init_done;
+ }
+
+ intf_type = mdp3_ctrl_get_intf_type(mfd);
+ mdp3_session->intf = mdp3_get_display_intf(intf_type);
+ if (!mdp3_session->intf) {
+ rc = -ENODEV;
+ goto init_done;
+ }
+
+ mdp3_session->panel = dev_get_platdata(&mfd->pdev->dev);
+ mdp3_session->status = 0;
+
+ mfd->mdp.private1 = mdp3_session;
+
+ rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
+ if (rc) {
+ pr_err("vsync sysfs group creation failed, ret=%d\n", rc);
+ goto init_done;
+ }
+
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+ pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
+
+init_done:
+ if (IS_ERR_VALUE(rc))
+ kfree(mdp3_session);
+
+ return rc;
+}
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
new file mode 100644
index 0000000..d42ece7
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDP3_CTRL_H
+#define MDP3_CTRL_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+
+#include "mdp3_dma.h"
+#include "mdss_fb.h"
+#include "mdss_panel.h"
+
+struct mdp3_session_data {
+ struct mutex lock;
+ int status;
+ struct mdp3_dma *dma;
+ struct mdss_panel_data *panel;
+ struct mdp3_intf *intf;
+ struct msm_fb_data_type *mfd;
+ struct completion vsync_comp;
+};
+
+int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
+
+#endif /* MDP3_CTRL_H */
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
new file mode 100644
index 0000000..69e3d7e
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -0,0 +1,914 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+
+#include "mdp3.h"
+#include "mdp3_dma.h"
+#include "mdp3_hwio.h"
+
+#define DMA_STOP_POLL_SLEEP_US 1000
+#define DMA_STOP_POLL_TIMEOUT_US 16000
+
+static ktime_t mdp3_get_vsync_time(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ ktime_t time;
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ time = dma->vsync_time;
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+ return time;
+}
+
+static void mdp3_vsync_intr_handler(int type, void *arg)
+{
+ struct mdp3_dma *dma = (struct mdp3_dma *)arg;
+ struct mdp3_vsync_notification vsync_client;
+
+ pr_debug("mdp3_vsync_intr_handler\n");
+ spin_lock(&dma->dma_lock);
+ vsync_client = dma->vsync_client;
+ if (!vsync_client.handler)
+ dma->cb_type &= ~MDP3_DMA_CALLBACK_TYPE_VSYNC;
+ dma->vsync_time = ktime_get();
+ complete(&dma->vsync_comp);
+ if (vsync_client.handler)
+ vsync_client.handler(vsync_client.arg);
+ spin_unlock(&dma->dma_lock);
+
+ if (!vsync_client.handler)
+ mdp3_irq_disable_nosync(type);
+}
+
+static void mdp3_dma_done_intr_handler(int type, void *arg)
+{
+ struct mdp3_dma *dma = (struct mdp3_dma *)arg;
+
+ pr_debug("mdp3_dma_done_intr_handler\n");
+ spin_lock(&dma->dma_lock);
+ dma->busy = false;
+ dma->cb_type &= ~MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
+ spin_unlock(&dma->dma_lock);
+ complete(&dma->dma_comp);
+ mdp3_irq_disable_nosync(type);
+}
+
+void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type)
+{
+ int irq_bit;
+ unsigned long flag;
+
+ pr_debug("mdp3_dma_callback_enable type=%d\n", type);
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if (dma->cb_type & type) {
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+ return;
+ } else {
+ dma->cb_type |= type;
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+ }
+
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
+ mdp3_irq_enable(MDP3_INTR_LCDC_START_OF_FRAME);
+ } else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC) {
+ irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE;
+ irq_bit += dma->dma_sel;
+ mdp3_irq_enable(irq_bit);
+ }
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_DMA_DONE) {
+ irq_bit = MDP3_INTR_DMA_P_DONE;
+ if (dma->dma_sel == MDP3_DMA_S)
+ irq_bit = MDP3_INTR_DMA_S_DONE;
+ mdp3_irq_enable(irq_bit);
+ }
+ } else {
+ pr_err("mdp3_dma_callback_enable not supported interface\n");
+ }
+}
+
+void mdp3_dma_callback_disable(struct mdp3_dma *dma, int type)
+{
+ int irq_bit;
+ unsigned long flag;
+
+ pr_debug("mdp3_dma_callback_disable type=%d\n", type);
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if ((dma->cb_type & type) == 0) {
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+ return;
+ } else {
+ dma->cb_type &= ~type;
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+ }
+
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
+ mdp3_irq_disable(MDP3_INTR_LCDC_START_OF_FRAME);
+ } else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC) {
+ irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE;
+ irq_bit += dma->dma_sel;
+ mdp3_irq_disable(irq_bit);
+ }
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_DMA_DONE) {
+ irq_bit = MDP3_INTR_DMA_P_DONE;
+ if (dma->dma_sel == MDP3_DMA_S)
+ irq_bit = MDP3_INTR_DMA_S_DONE;
+ mdp3_irq_disable(irq_bit);
+ }
+ }
+}
+
+static int mdp3_dma_callback_setup(struct mdp3_dma *dma)
+{
+ int rc;
+ struct mdp3_intr_cb vsync_cb = {
+ .cb = mdp3_vsync_intr_handler,
+ .data = dma,
+ };
+
+ struct mdp3_intr_cb dma_cb = {
+ .cb = mdp3_dma_done_intr_handler,
+ .data = dma,
+ };
+
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
+ dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC)
+ rc = mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
+ &vsync_cb);
+ else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ int irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE;
+ irq_bit += dma->dma_sel;
+ rc = mdp3_set_intr_callback(irq_bit, &vsync_cb);
+ irq_bit = MDP3_INTR_DMA_P_DONE;
+ if (dma->dma_sel == MDP3_DMA_S)
+ irq_bit = MDP3_INTR_DMA_S_DONE;
+ rc |= mdp3_set_intr_callback(irq_bit, &dma_cb);
+ } else {
+ pr_err("mdp3_dma_callback_setup not suppported interface\n");
+ rc = -ENODEV;
+ }
+ return rc;
+}
+
+static void mdp3_dma_vsync_enable(struct mdp3_dma *dma,
+ struct mdp3_vsync_notification *vsync_client)
+{
+ unsigned long flag;
+ int updated = 0;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
+
+ pr_debug("mdp3_dma_vsync_enable\n");
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if (vsync_client) {
+ if (dma->vsync_client.handler != vsync_client->handler) {
+ dma->vsync_client = *vsync_client;
+ updated = 1;
+ }
+ } else {
+ if (!dma->vsync_client.handler) {
+ dma->vsync_client.handler = NULL;
+ dma->vsync_client.arg = NULL;
+ updated = 1;
+ }
+ }
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+
+ if (updated) {
+ if (vsync_client && vsync_client->handler)
+ mdp3_dma_callback_enable(dma, cb_type);
+ else
+ mdp3_dma_callback_disable(dma, cb_type);
+ }
+}
+
+static int mdp3_dmap_config(struct mdp3_dma *dma,
+ struct mdp3_dma_source *source_config,
+ struct mdp3_dma_output_config *output_config)
+{
+ u32 dma_p_cfg_reg, dma_p_size, dma_p_out_xy;
+
+ dma_p_cfg_reg = source_config->format << 25;
+ if (output_config->dither_en)
+ dma_p_cfg_reg |= BIT(24);
+ dma_p_cfg_reg |= output_config->out_sel << 19;
+ dma_p_cfg_reg |= output_config->bit_mask_polarity << 18;
+ dma_p_cfg_reg |= output_config->color_components_flip << 14;
+ dma_p_cfg_reg |= output_config->pack_pattern << 8;
+ dma_p_cfg_reg |= output_config->pack_align << 7;
+ dma_p_cfg_reg |= output_config->color_comp_out_bits;
+
+ 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);
+
+ /*
+ * NOTE: MDP_DMA_P_FETCH_CFG: max_burst_size need to use value 4, not
+ * the default 16 for MDP hang issue workaround
+ */
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_FETCH_CFG, 0x10);
+ MDP3_REG_WRITE(MDP3_REG_PRIMARY_RD_PTR_IRQ, 0x10);
+
+ dma->source_config = *source_config;
+ dma->output_config = *output_config;
+
+ mdp3_dma_callback_setup(dma);
+ return 0;
+}
+
+static int mdp3_dmas_config(struct mdp3_dma *dma,
+ struct mdp3_dma_source *source_config,
+ struct mdp3_dma_output_config *output_config)
+{
+ u32 dma_s_cfg_reg, dma_s_size, dma_s_out_xy;
+
+ dma_s_cfg_reg = source_config->format << 25;
+ if (output_config->dither_en)
+ dma_s_cfg_reg |= BIT(24);
+ dma_s_cfg_reg |= output_config->out_sel << 19;
+ dma_s_cfg_reg |= output_config->bit_mask_polarity << 18;
+ dma_s_cfg_reg |= output_config->color_components_flip << 14;
+ dma_s_cfg_reg |= output_config->pack_pattern << 8;
+ dma_s_cfg_reg |= output_config->pack_align << 7;
+ dma_s_cfg_reg |= output_config->color_comp_out_bits;
+
+ 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);
+
+ dma->source_config = *source_config;
+ dma->output_config = *output_config;
+
+ mdp3_dma_callback_setup(dma);
+ return 0;
+}
+
+static int mdp3_dmap_cursor_config(struct mdp3_dma *dma,
+ struct mdp3_dma_cursor *cursor)
+{
+ u32 cursor_size, cursor_pos, blend_param, trans_mask;
+
+ cursor_size = cursor->width | (cursor->height << 16);
+ cursor_pos = cursor->x | (cursor->y << 16);
+ trans_mask = 0;
+ if (cursor->blend_config.mode == MDP3_DMA_CURSOR_BLEND_CONSTANT_ALPHA) {
+ blend_param = cursor->blend_config.constant_alpha << 24;
+ } else if (cursor->blend_config.mode ==
+ MDP3_DMA_CURSOR_BLEND_COLOR_KEYING) {
+ blend_param = cursor->blend_config.transparent_color;
+ trans_mask = cursor->blend_config.transparency_mask;
+ } else {
+ blend_param = 0;
+ }
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_FORMAT, cursor->format);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_SIZE, cursor_size);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BUF_ADDR, (u32)cursor->buf);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_POS, cursor_pos);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BLEND_CONFIG,
+ cursor->blend_config.mode);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BLEND_PARAM, blend_param);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_BLEND_TRANS_MASK, trans_mask);
+ dma->cursor = *cursor;
+ return 0;
+}
+
+static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
+ struct mdp3_dma_color_correct_config *config,
+ struct mdp3_dma_ccs *ccs,
+ struct mdp3_dma_lut *lut)
+{
+ int i;
+ u32 addr, cc_config, color;
+
+ cc_config = config->lut_enable;
+ if (config->ccs_enable)
+ cc_config |= BIT(3);
+ cc_config |= config->lut_position << 4;
+ cc_config |= config->ccs_sel << 5;
+ cc_config |= config->pre_bias_sel << 6;
+ cc_config |= config->post_bias_sel << 7;
+ cc_config |= config->pre_limit_sel << 8;
+ cc_config |= config->post_limit_sel << 9;
+ cc_config |= config->lut_sel << 10;
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
+
+ if (config->ccs_enable && ccs) {
+ if (ccs->mv1) {
+ addr = MDP3_REG_DMA_P_CSC_MV1;
+ for (i = 0; i < 9; i++) {
+ MDP3_REG_WRITE(addr, ccs->mv1[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->mv2) {
+ addr = MDP3_REG_DMA_P_CSC_MV2;
+ for (i = 0; i < 9; i++) {
+ MDP3_REG_WRITE(addr, ccs->mv2[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->pre_bv1) {
+ addr = MDP3_REG_DMA_P_CSC_PRE_BV1;
+ for (i = 0; i < 3; i++) {
+ MDP3_REG_WRITE(addr, ccs->pre_bv1[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->pre_bv2) {
+ addr = MDP3_REG_DMA_P_CSC_PRE_BV2;
+ for (i = 0; i < 3; i++) {
+ MDP3_REG_WRITE(addr, ccs->pre_bv2[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->post_bv1) {
+ addr = MDP3_REG_DMA_P_CSC_POST_BV1;
+ for (i = 0; i < 3; i++) {
+ MDP3_REG_WRITE(addr, ccs->post_bv1[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->post_bv2) {
+ addr = MDP3_REG_DMA_P_CSC_POST_BV2;
+ for (i = 0; i < 3; i++) {
+ MDP3_REG_WRITE(addr, ccs->post_bv2[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->pre_lv1) {
+ addr = MDP3_REG_DMA_P_CSC_PRE_LV1;
+ for (i = 0; i < 6; i++) {
+ MDP3_REG_WRITE(addr, ccs->pre_lv1[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->pre_lv2) {
+ addr = MDP3_REG_DMA_P_CSC_PRE_LV2;
+ for (i = 0; i < 6; i++) {
+ MDP3_REG_WRITE(addr, ccs->pre_lv2[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->post_lv1) {
+ addr = MDP3_REG_DMA_P_CSC_POST_LV1;
+ for (i = 0; i < 6; i++) {
+ MDP3_REG_WRITE(addr, ccs->post_lv1[i]);
+ addr += 4;
+ }
+ }
+
+ if (ccs->post_lv2) {
+ addr = MDP3_REG_DMA_P_CSC_POST_LV2;
+ for (i = 0; i < 6; i++) {
+ MDP3_REG_WRITE(addr, ccs->post_lv2[i]);
+ addr += 4;
+ }
+ }
+ }
+
+ if (config->lut_enable && lut) {
+ if (lut->color0_lut1 && lut->color1_lut1 && lut->color2_lut1) {
+ addr = MDP3_REG_DMA_P_CSC_LUT1;
+ for (i = 0; i < 256; i++) {
+ color = lut->color0_lut1[i];
+ color |= lut->color1_lut1[i] << 8;
+ color |= lut->color2_lut1[i] << 16;
+ MDP3_REG_WRITE(addr, color);
+ addr += 4;
+ }
+ }
+
+ if (lut->color0_lut2 && lut->color1_lut2 && lut->color2_lut2) {
+ addr = MDP3_REG_DMA_P_CSC_LUT2;
+ for (i = 0; i < 256; i++) {
+ color = lut->color0_lut2[i];
+ color |= lut->color1_lut2[i] << 8;
+ color |= lut->color2_lut2[i] << 16;
+ MDP3_REG_WRITE(addr, color);
+ addr += 4;
+ }
+ }
+ }
+
+ dma->ccs_config = *config;
+ return 0;
+}
+
+static int mdp3_dmap_histo_config(struct mdp3_dma *dma,
+ struct mdp3_dma_histogram_config *histo_config)
+{
+ u32 hist_bit_mask, hist_control;
+
+ if (histo_config->bit_mask_polarity)
+ hist_bit_mask = BIT(31);
+ hist_bit_mask |= histo_config->bit_mask;
+
+ if (histo_config->auto_clear_en)
+ hist_control = BIT(0);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_FRAME_CNT,
+ histo_config->frame_count);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, hist_bit_mask);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, hist_control);
+ return 0;
+}
+
+static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf)
+{
+ int wait_for_dma_done = 0;
+ unsigned long flag;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
+
+ 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;
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if (dma->busy)
+ wait_for_dma_done = 1;
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+
+ if (wait_for_dma_done)
+ wait_for_completion_killable(&dma->dma_comp);
+ }
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
+ dma->source_config.buf = buf;
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_START, 1);
+ dma->busy = true;
+ }
+ wmb();
+ init_completion(&dma->vsync_comp);
+ 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");
+ wait_for_completion_killable(&dma->vsync_comp);
+ pr_debug("mdp3_dmap_update wait for vsync_comp out\n");
+ return 0;
+}
+
+static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf)
+{
+ int wait_for_dma_done = 0;
+ unsigned long flag;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
+
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if (dma->busy)
+ wait_for_dma_done = 1;
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+
+ if (wait_for_dma_done)
+ wait_for_completion_killable(&dma->dma_comp);
+ }
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_IBUF_ADDR, (u32)buf);
+ dma->source_config.buf = buf;
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ MDP3_REG_WRITE(MDP3_REG_DMA_S_START, 1);
+ dma->busy = true;
+ }
+ wmb();
+ init_completion(&dma->vsync_comp);
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+
+ mdp3_dma_callback_enable(dma, cb_type);
+ wait_for_completion_killable(&dma->vsync_comp);
+ return 0;
+}
+
+static int mdp3_dmap_cursor_update(struct mdp3_dma *dma, int x, int y)
+{
+ u32 cursor_pos;
+
+ cursor_pos = x | (y << 16);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_CURSOR_POS, cursor_pos);
+ dma->cursor.x = x;
+ dma->cursor.y = y;
+ return 0;
+}
+
+static int mdp3_dmap_histo_get(struct mdp3_dma *dma,
+ struct mdp3_dma_histogram_data *data)
+{
+ int i;
+ u32 addr, extra;
+
+ addr = MDP3_REG_DMA_P_HIST_R_DATA;
+ for (i = 0; i < 32; i++) {
+ data->r_data[i] = MDP3_REG_READ(addr);
+ addr += 4;
+ }
+
+ addr = MDP3_REG_DMA_P_HIST_G_DATA;
+ for (i = 0; i < 32; i++) {
+ data->g_data[i] = MDP3_REG_READ(addr);
+ addr += 4;
+ }
+
+ addr = MDP3_REG_DMA_P_HIST_B_DATA;
+ for (i = 0; i < 32; i++) {
+ data->b_data[i] = MDP3_REG_READ(addr);
+ addr += 4;
+ }
+
+ extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
+ data->r_min_value = (extra & 0x1F0000) >> 16;
+ data->r_max_value = (extra & 0x1F000000) >> 24;
+ extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
+ data->g_min_value = extra & 0x1F;
+ data->g_max_value = (extra & 0x1F00) >> 8;
+ data->b_min_value = (extra & 0x1F0000) >> 16;
+ data->b_max_value = (extra & 0x1F000000) >> 24;
+ return 0;
+}
+
+static int mdp3_dmap_histo_op(struct mdp3_dma *dma, u32 op)
+{
+ switch (op) {
+ case MDP3_DMA_HISTO_OP_START:
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ break;
+ case MDP3_DMA_HISTO_OP_STOP:
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_STOP_REQ, 1);
+ break;
+ case MDP3_DMA_HISTO_OP_CANCEL:
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ break;
+ case MDP3_DMA_HISTO_OP_RESET:
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mdp3_dmap_histo_intr_status(struct mdp3_dma *dma, int *status)
+{
+ *status = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
+ return 0;
+}
+
+static int mdp3_dmap_histo_intr_enable(struct mdp3_dma *dma, u32 mask)
+{
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, mask);
+ return 0;
+}
+
+static int mdp3_dmap_histo_intr_clear(struct mdp3_dma *dma, u32 mask)
+{
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, mask);
+ return 0;
+}
+
+static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf)
+{
+ unsigned long flag;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
+ u32 dma_start_offset = MDP3_REG_DMA_P_START;
+
+ if (dma->dma_sel == MDP3_DMA_P)
+ dma_start_offset = MDP3_REG_DMA_P_START;
+ else if (dma->dma_sel == MDP3_DMA_S)
+ dma_start_offset = MDP3_REG_DMA_S_START;
+ else
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
+ MDP3_REG_WRITE(dma_start_offset, 1);
+ dma->busy = true;
+ }
+
+ intf->start(intf);
+ wmb();
+ init_completion(&dma->vsync_comp);
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+
+ mdp3_dma_callback_enable(dma, cb_type);
+ pr_debug("mdp3_dma_start wait for vsync_comp in\n");
+ wait_for_completion_killable(&dma->vsync_comp);
+ pr_debug("mdp3_dma_start wait for vsync_comp out\n");
+ return 0;
+}
+
+static int mdp3_dma_stop(struct mdp3_dma *dma, struct mdp3_intf *intf)
+{
+ int ret = 0;
+ u32 status, display_status_bit;
+
+ if (dma->dma_sel == MDP3_DMA_P)
+ display_status_bit = BIT(6);
+ else if (dma->dma_sel == MDP3_DMA_S)
+ display_status_bit = BIT(7);
+ else
+ return -EINVAL;
+
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
+ display_status_bit |= BIT(11);
+
+ intf->stop(intf);
+ ret = readl_poll_timeout((mdp3_res->mdp_base + MDP3_REG_DISPLAY_STATUS),
+ status,
+ ((status & display_status_bit) == 0),
+ DMA_STOP_POLL_SLEEP_US,
+ DMA_STOP_POLL_TIMEOUT_US);
+
+ mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_VSYNC |
+ MDP3_DMA_CALLBACK_TYPE_DMA_DONE);
+
+ dma->busy = false;
+ return ret;
+}
+
+int mdp3_dma_init(struct mdp3_dma *dma,
+ struct mdp3_dma_source *source_config,
+ struct mdp3_dma_output_config *output_config)
+{
+ int ret = 0;
+
+ pr_debug("mdp3_dma_init\n");
+ switch (dma->dma_sel) {
+ case MDP3_DMA_P:
+ dma->busy = 0;
+
+ ret = mdp3_dmap_config(dma, source_config, output_config);
+ if (ret < 0)
+ return ret;
+
+ dma->config_cursor = mdp3_dmap_cursor_config;
+ dma->config_ccs = mdp3_dmap_ccs_config;
+ dma->config_histo = mdp3_dmap_histo_config;
+ dma->update = mdp3_dmap_update;
+ dma->update_cursor = mdp3_dmap_cursor_update;
+ dma->get_histo = mdp3_dmap_histo_get;
+ dma->histo_op = mdp3_dmap_histo_op;
+ dma->histo_intr_status = mdp3_dmap_histo_intr_status;
+ dma->histo_intr_enable = mdp3_dmap_histo_intr_enable;
+ dma->histo_intr_clear = mdp3_dmap_histo_intr_clear;
+ dma->vsync_enable = mdp3_dma_vsync_enable;
+ dma->get_vsync_time = mdp3_get_vsync_time;
+ dma->start = mdp3_dma_start;
+ dma->stop = mdp3_dma_stop;
+ break;
+ case MDP3_DMA_S:
+ dma->busy = 0;
+ ret = mdp3_dmas_config(dma, source_config, output_config);
+ if (ret < 0)
+ return ret;
+
+ dma->config_cursor = NULL;
+ dma->config_ccs = NULL;
+ dma->config_histo = NULL;
+ dma->update = mdp3_dmas_update;
+ dma->update_cursor = NULL;
+ dma->get_histo = NULL;
+ dma->histo_op = NULL;
+ dma->histo_intr_status = NULL;
+ dma->histo_intr_enable = NULL;
+ dma->histo_intr_clear = NULL;
+ dma->vsync_enable = mdp3_dma_vsync_enable;
+ dma->get_vsync_time = mdp3_get_vsync_time;
+ dma->start = mdp3_dma_start;
+ dma->stop = mdp3_dma_stop;
+ break;
+ case MDP3_DMA_E:
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ spin_lock_init(&dma->dma_lock);
+ init_completion(&dma->vsync_comp);
+ init_completion(&dma->dma_comp);
+ dma->cb_type = 0;
+ dma->vsync_client.handler = NULL;
+ dma->vsync_client.arg = NULL;
+
+ memset(&dma->cursor, 0, sizeof(dma->cursor));
+ memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
+ memset(&dma->histogram_config, 0, sizeof(dma->histogram_config));
+
+ return ret;
+}
+
+int lcdc_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg)
+{
+ u32 temp;
+ struct mdp3_video_intf_cfg *v = &cfg->video;
+ temp = v->hsync_pulse_width | (v->hsync_period << 16);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_HSYNC_CTL, temp);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_VSYNC_PERIOD, v->vsync_period);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_VSYNC_PULSE_WIDTH, v->vsync_pulse_width);
+ temp = v->display_start_x | (v->display_end_x << 16);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_DISPLAY_HCTL, temp);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_DISPLAY_V_START, v->display_start_y);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_DISPLAY_V_END, v->display_end_y);
+ temp = v->active_start_x | (v->active_end_x);
+ if (v->active_h_enable)
+ temp |= BIT(31);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_ACTIVE_HCTL, temp);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_ACTIVE_V_START, v->active_start_y);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_ACTIVE_V_END, v->active_end_y);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_HSYNC_SKEW, v->hsync_skew);
+ temp = 0;
+ if (!v->hsync_polarity)
+ temp = BIT(0);
+ if (!v->vsync_polarity)
+ temp = BIT(1);
+ if (!v->de_polarity)
+ temp = BIT(2);
+ MDP3_REG_WRITE(MDP3_REG_LCDC_CTL_POLARITY, temp);
+
+ return 0;
+}
+
+int lcdc_start(struct mdp3_intf *intf)
+{
+ MDP3_REG_WRITE(MDP3_REG_LCDC_EN, BIT(0));
+ wmb();
+ intf->active = true;
+ return 0;
+}
+
+int lcdc_stop(struct mdp3_intf *intf)
+{
+ MDP3_REG_WRITE(MDP3_REG_LCDC_EN, 0);
+ wmb();
+ intf->active = false;
+ return 0;
+}
+
+int dsi_video_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg)
+{
+ u32 temp;
+ struct mdp3_video_intf_cfg *v = &cfg->video;
+
+ pr_debug("dsi_video_config\n");
+
+ temp = v->hsync_pulse_width | (v->hsync_period << 16);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_HSYNC_CTL, temp);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_VSYNC_PERIOD, v->vsync_period);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_VSYNC_PULSE_WIDTH,
+ v->vsync_pulse_width);
+ temp = v->display_start_x | (v->display_end_x << 16);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_DISPLAY_HCTL, temp);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_DISPLAY_V_START, v->display_start_y);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_DISPLAY_V_END, v->display_end_y);
+ temp = v->active_start_x | (v->active_end_x << 16);
+ if (v->active_h_enable)
+ temp |= BIT(31);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_ACTIVE_HCTL, temp);
+
+ temp = v->active_start_y;
+ if (v->active_v_enable)
+ temp |= BIT(31);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_ACTIVE_V_START, temp);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_ACTIVE_V_END, v->active_end_y);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_HSYNC_SKEW, v->hsync_skew);
+ temp = 0;
+ if (!v->hsync_polarity)
+ temp |= BIT(0);
+ if (!v->vsync_polarity)
+ temp |= BIT(1);
+ if (!v->de_polarity)
+ temp |= BIT(2);
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_CTL_POLARITY, temp);
+
+ return 0;
+}
+
+int dsi_video_start(struct mdp3_intf *intf)
+{
+ pr_debug("dsi_video_start\n");
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, BIT(0));
+ wmb();
+ intf->active = true;
+ return 0;
+}
+
+int dsi_video_stop(struct mdp3_intf *intf)
+{
+ pr_debug("dsi_video_stop\n");
+ MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, 0);
+ wmb();
+ intf->active = false;
+ return 0;
+}
+
+int dsi_cmd_config(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg)
+{
+ u32 id_map = 0;
+ u32 trigger_en = 0;
+
+ if (cfg->dsi_cmd.primary_dsi_cmd_id)
+ id_map = BIT(0);
+ if (cfg->dsi_cmd.secondary_dsi_cmd_id)
+ id_map = BIT(4);
+
+ if (cfg->dsi_cmd.dsi_cmd_tg_intf_sel)
+ trigger_en = BIT(4);
+
+ MDP3_REG_WRITE(MDP3_REG_DSI_CMD_MODE_ID_MAP, id_map);
+ MDP3_REG_WRITE(MDP3_REG_DSI_CMD_MODE_TRIGGER_EN, trigger_en);
+
+ return 0;
+}
+
+int dsi_cmd_start(struct mdp3_intf *intf)
+{
+ intf->active = true;
+ return 0;
+}
+
+int dsi_cmd_stop(struct mdp3_intf *intf)
+{
+ intf->active = false;
+ return 0;
+}
+
+int mdp3_intf_init(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg)
+{
+ int ret = 0;
+ switch (cfg->type) {
+ case MDP3_DMA_OUTPUT_SEL_LCDC:
+ intf->config = lcdc_config;
+ intf->start = lcdc_start;
+ intf->stop = lcdc_stop;
+ break;
+ case MDP3_DMA_OUTPUT_SEL_DSI_VIDEO:
+ intf->config = dsi_video_config;
+ intf->start = dsi_video_start;
+ intf->stop = dsi_video_stop;
+ break;
+ case MDP3_DMA_OUTPUT_SEL_DSI_CMD:
+ intf->config = dsi_cmd_config;
+ intf->start = dsi_cmd_start;
+ intf->stop = dsi_cmd_stop;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ intf->active = false;
+ if (intf->config)
+ ret = intf->config(intf, cfg);
+
+ if (ret) {
+ pr_err("MDP interface initialization failed\n");
+ return ret;
+ }
+
+ intf->cfg = *cfg;
+ return 0;
+}
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
new file mode 100644
index 0000000..2fb8427
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -0,0 +1,336 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDP3_DMA_H
+#define MDP3_DMA_H
+
+#include <linux/sched.h>
+
+enum {
+ MDP3_DMA_P,
+ MDP3_DMA_S,
+ MDP3_DMA_E,
+ MDP3_DMA_MAX
+};
+
+enum {
+ MDP3_DMA_CAP_CURSOR = 0x1,
+ MDP3_DMA_CAP_COLOR_CORRECTION = 0x2,
+ MDP3_DMA_CAP_HISTOGRAM = 0x4,
+ MDP3_DMA_CAP_GAMMA_CORRECTION = 0x8,
+ MDP3_DMA_CAP_DITHER = 0x10,
+ MDP3_DMA_CAP_ALL = 0x1F
+};
+
+enum {
+ MDP3_DMA_OUTPUT_SEL_AHB,
+ MDP3_DMA_OUTPUT_SEL_DSI_CMD,
+ MDP3_DMA_OUTPUT_SEL_LCDC,
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO,
+ MDP3_DMA_OUTPUT_SEL_MAX
+};
+
+enum {
+ MDP3_DMA_IBUF_FORMAT_RGB888,
+ MDP3_DMA_IBUF_FORMAT_RGB565,
+ MDP3_DMA_IBUF_FORMAT_XRGB8888,
+ MDP3_DMA_IBUF_FORMAT_UNDEFINED
+};
+
+enum {
+ MDP3_DMA_OUTPUT_PACK_PATTERN_RGB = 0x21,
+ MDP3_DMA_OUTPUT_PACK_PATTERN_RBG = 0x24,
+ MDP3_DMA_OUTPUT_PACK_PATTERN_BGR = 0x12,
+ MDP3_DMA_OUTPUT_PACK_PATTERN_BRG = 0x18,
+ MDP3_DMA_OUTPUT_PACK_PATTERN_GBR = 0x06,
+ MDP3_DMA_OUTPUT_PACK_PATTERN_GRB = 0x09,
+};
+
+enum {
+ MDP3_DMA_OUTPUT_PACK_ALIGN_LSB,
+ MDP3_DMA_OUTPUT_PACK_ALIGN_MSB
+};
+
+enum {
+ MDP3_DMA_OUTPUT_COMP_BITS_4, /*4 bits per color component*/
+ MDP3_DMA_OUTPUT_COMP_BITS_5,
+ MDP3_DMA_OUTPUT_COMP_BITS_6,
+ MDP3_DMA_OUTPUT_COMP_BITS_8,
+};
+
+enum {
+ MDP3_DMA_CURSOR_FORMAT_ARGB888,
+};
+
+enum {
+ MDP3_DMA_COLOR_CORRECT_SET_1,
+ MDP3_DMA_COLOR_CORRECT_SET_2
+};
+
+enum {
+ MDP3_DMA_LUT_POSITION_PRE,
+ MDP3_DMA_LUT_POSITION_POST
+};
+
+enum {
+ MDP3_DMA_LUT_DISABLE = 0x0,
+ MDP3_DMA_LUT_ENABLE_C0 = 0x01,
+ MDP3_DMA_LUT_ENABLE_C1 = 0x02,
+ MDP3_DMA_LUT_ENABLE_C2 = 0x04,
+ MDP3_DMA_LUT_ENABLE_ALL = 0x07,
+};
+
+enum {
+ MDP3_DMA_HISTOGRAM_BIT_MASK_NONE = 0X0,
+ MDP3_DMA_HISTOGRAM_BIT_MASK_ONE_MSB = 0x1,
+ MDP3_DMA_HISTOGRAM_BIT_MASK_TWO_MSB = 0x2,
+ MDP3_DMA_HISTOGRAM_BIT_MASK_THREE_MSB = 0x3
+};
+
+enum {
+ MDP3_DMA_COLOR_FLIP_NONE,
+ MDP3_DMA_COLOR_FLIP_COMP1 = 0x1,
+ MDP3_DMA_COLOR_FLIP_COMP2 = 0x2,
+ MDP3_DMA_COLOR_FLIP_COMP3 = 0x4,
+};
+
+enum {
+ MDP3_DMA_CURSOR_BLEND_NONE = 0x0,
+ MDP3_DMA_CURSOR_BLEND_PER_PIXEL_ALPHA = 0x3,
+ MDP3_DMA_CURSOR_BLEND_CONSTANT_ALPHA = 0x5,
+ MDP3_DMA_CURSOR_BLEND_COLOR_KEYING = 0x9
+};
+
+enum {
+ MDP3_DMA_HISTO_OP_START,
+ MDP3_DMA_HISTO_OP_STOP,
+ MDP3_DMA_HISTO_OP_CANCEL,
+ MDP3_DMA_HISTO_OP_RESET
+};
+
+enum {
+ MDP3_DMA_CALLBACK_TYPE_VSYNC = 0x01,
+ MDP3_DMA_CALLBACK_TYPE_DMA_DONE = 0x02,
+};
+
+struct mdp3_dma_source {
+ u32 format;
+ int width;
+ int height;
+ int x;
+ int y;
+ void *buf;
+ int stride;
+};
+
+struct mdp3_dma_output_config {
+ int dither_en;
+ u32 out_sel;
+ u32 bit_mask_polarity;
+ u32 color_components_flip;
+ u32 pack_pattern;
+ u32 pack_align;
+ u32 color_comp_out_bits;
+};
+
+struct mdp3_dma_cursor_blend_config {
+ u32 mode;
+ u32 transparent_color; /*color keying*/
+ u32 transparency_mask;
+ u32 constant_alpha;
+};
+
+struct mdp3_dma_cursor {
+ int enable; /* enable cursor or not*/
+ u32 format;
+ int width;
+ int height;
+ int x;
+ int y;
+ void *buf;
+ struct mdp3_dma_cursor_blend_config blend_config;
+};
+
+struct mdp3_dma_ccs {
+ u32 *mv1; /*set1 matrix vector, 3x3 */
+ u32 *mv2;
+ u32 *pre_bv1; /*pre-bias vector for set1, 1x3*/
+ u32 *pre_bv2;
+ u32 *post_bv1; /*post-bias vecotr for set1, */
+ u32 *post_bv2;
+ u32 *pre_lv1; /*pre-limit vector for set 1, 1x6*/
+ u32 *pre_lv2;
+ u32 *post_lv1;
+ u32 *post_lv2;
+};
+
+struct mdp3_dma_lut {
+ uint8_t *color0_lut1;
+ uint8_t *color1_lut1;
+ uint8_t *color2_lut1;
+ uint8_t *color0_lut2;
+ uint8_t *color1_lut2;
+ uint8_t *color2_lut2;
+};
+
+struct mdp3_dma_color_correct_config {
+ int ccs_enable;
+ int lut_enable;
+ u32 lut_sel;
+ u32 post_limit_sel;
+ u32 pre_limit_sel;
+ u32 post_bias_sel;
+ u32 pre_bias_sel;
+ u32 ccs_sel;
+ u32 lut_position;
+};
+
+struct mdp3_dma_histogram_config {
+ int frame_count;
+ u32 bit_mask_polarity;
+ u32 bit_mask;
+ int auto_clear_en;
+};
+
+struct mdp3_dma_histogram_data {
+ uint8_t r_max_value;
+ uint8_t r_min_value;
+ uint8_t b_max_value;
+ uint8_t b_min_value;
+ uint8_t g_max_value;
+ uint8_t g_min_value;
+ uint8_t r_data[32];
+ uint8_t g_data[32];
+ uint8_t b_data[32];
+};
+
+struct mdp3_vsync_notification {
+ void (*handler)(void *arg);
+ void *arg;
+};
+
+struct mdp3_intf;
+
+struct mdp3_dma {
+ u32 dma_sel;
+ u32 capability;
+ int in_use;
+ int available;
+ int busy;
+
+ spinlock_t dma_lock;
+ struct completion vsync_comp;
+ struct completion dma_comp;
+ ktime_t vsync_time;
+ struct mdp3_vsync_notification vsync_client;
+ u32 cb_type;
+
+ struct mdp3_dma_output_config output_config;
+ struct mdp3_dma_source source_config;
+
+ struct mdp3_dma_cursor cursor;
+ struct mdp3_dma_color_correct_config ccs_config;
+ struct mdp3_dma_histogram_config histogram_config;
+
+ int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf);
+
+ int (*stop)(struct mdp3_dma *dma, struct mdp3_intf *intf);
+
+ int (*config_cursor)(struct mdp3_dma *dma,
+ struct mdp3_dma_cursor *cursor);
+
+ int (*config_ccs)(struct mdp3_dma *dma,
+ struct mdp3_dma_color_correct_config *config,
+ struct mdp3_dma_ccs *ccs,
+ struct mdp3_dma_lut *lut);
+
+ int (*update)(struct mdp3_dma *dma, void *buf);
+
+ int (*update_cursor)(struct mdp3_dma *dma, int x, int y);
+
+ int (*get_histo)(struct mdp3_dma *dma,
+ struct mdp3_dma_histogram_data *data);
+
+ int (*config_histo)(struct mdp3_dma *dma,
+ struct mdp3_dma_histogram_config *histo_config);
+
+ int (*histo_op)(struct mdp3_dma *dma,
+ u32 op);
+
+ int (*histo_intr_status)(struct mdp3_dma *dma, int *status);
+
+ int (*histo_intr_enable)(struct mdp3_dma *dma, u32 mask);
+
+ int (*histo_intr_clear)(struct mdp3_dma *dma, u32 mask);
+
+ void (*vsync_enable)(struct mdp3_dma *dma,
+ struct mdp3_vsync_notification *vsync_client);
+
+ ktime_t (*get_vsync_time)(struct mdp3_dma *dma);
+
+};
+
+struct mdp3_video_intf_cfg {
+ int hsync_period;
+ int hsync_pulse_width;
+ int vsync_period;
+ int vsync_pulse_width;
+ int display_start_x;
+ int display_end_x;
+ int display_start_y;
+ int display_end_y;
+ int active_start_x;
+ int active_end_x;
+ int active_h_enable;
+ int active_start_y;
+ int active_end_y;
+ int active_v_enable;
+ int hsync_skew;
+ int hsync_polarity;
+ int vsync_polarity;
+ int de_polarity;
+};
+
+struct mdp3_dsi_cmd_intf_cfg {
+ int primary_dsi_cmd_id;
+ int secondary_dsi_cmd_id;
+ int dsi_cmd_tg_intf_sel;
+};
+
+struct mdp3_intf_cfg {
+ u32 type;
+ struct mdp3_video_intf_cfg video;
+ struct mdp3_dsi_cmd_intf_cfg dsi_cmd;
+};
+
+struct mdp3_intf {
+ struct mdp3_intf_cfg cfg;
+ int active;
+ int available;
+ int in_use;
+ int (*config)(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg);
+ int (*start)(struct mdp3_intf *intf);
+ int (*stop)(struct mdp3_intf *intf);
+};
+
+int mdp3_dma_init(struct mdp3_dma *dma,
+ struct mdp3_dma_source *source_config,
+ struct mdp3_dma_output_config *output_config);
+
+int mdp3_intf_init(struct mdp3_intf *intf, struct mdp3_intf_cfg *cfg);
+
+void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type);
+
+void mdp3_dma_callback_disable(struct mdp3_dma *dma, int type);
+
+#endif /* MDP3_DMA_H */
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h
new file mode 100644
index 0000000..2763f46
--- /dev/null
+++ b/drivers/video/msm/mdss/mdp3_hwio.h
@@ -0,0 +1,216 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MDP3_HWIO_H
+#define MDP3_HWIO_H
+
+#include <linux/bitops.h>
+
+/*synchronization*/
+#define MDP3_REG_SYNC_CONFIG_0 0x0300
+#define MDP3_REG_SYNC_CONFIG_1 0x0304
+#define MDP3_REG_SYNC_CONFIG_2 0x0308
+#define MDP3_REG_SYNC_STATUS_0 0x030c
+#define MDP3_REG_SYNC_STATUS_1 0x0310
+#define MDP3_REG_SYNC_STATUS_2 0x0314
+#define MDP3_REG_PRIMARY_VSYNC_OUT_CTRL 0x0318
+#define MDP3_REG_SECONDARY_VSYNC_OUT_CTRL 0x031c
+#define MDP3_REG_EXTERNAL_VSYNC_OUT_CTRL 0x0320
+#define MDP3_REG_VSYNC_SEL 0x0324
+
+/*interrupt*/
+#define MDP3_REG_INTR_ENABLE 0x0020
+#define MDP3_REG_INTR_STATUS 0x0024
+#define MDP3_REG_INTR_CLEAR 0x0028
+
+#define MDP3_REG_PRIMARY_RD_PTR_IRQ 0x021C
+#define MDP3_REG_SECONDARY_RD_PTR_IRQ 0x0220
+
+/*operation control*/
+#define MDP3_REG_DMA_P_START 0x0044
+#define MDP3_REG_DMA_S_START 0x0048
+#define MDP3_REG_DMA_E_START 0x004c
+
+#define MDP3_REG_DISPLAY_STATUS 0x0038
+
+#define MDP3_REG_HW_VERSION 0x0070
+#define MDP3_REG_SW_RESET 0x0074
+
+/*EBI*/
+#define MDP3_REG_EBI2_LCD0 0x003c
+#define MDP3_REG_EBI2_LCD0_YSTRIDE 0x0050
+
+/*DMA_P*/
+#define MDP3_REG_DMA_P_CONFIG 0x90000
+#define MDP3_REG_DMA_P_SIZE 0x90004
+#define MDP3_REG_DMA_P_IBUF_ADDR 0x90008
+#define MDP3_REG_DMA_P_IBUF_Y_STRIDE 0x9000C
+#define MDP3_REG_DMA_P_PROFILE_EN 0x90020
+#define MDP3_REG_DMA_P_OUT_XY 0x90010
+#define MDP3_REG_DMA_P_CURSOR_FORMAT 0x90040
+#define MDP3_REG_DMA_P_CURSOR_SIZE 0x90044
+#define MDP3_REG_DMA_P_CURSOR_BUF_ADDR 0x90048
+#define MDP3_REG_DMA_P_CURSOR_POS 0x9004c
+#define MDP3_REG_DMA_P_CURSOR_BLEND_CONFIG 0x90060
+#define MDP3_REG_DMA_P_CURSOR_BLEND_PARAM 0x90064
+#define MDP3_REG_DMA_P_CURSOR_BLEND_TRANS_MASK 0x90068
+#define MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG 0x90070
+#define MDP3_REG_DMA_P_CSC_BYPASS 0X93004
+#define MDP3_REG_DMA_P_CSC_MV1 0x93400
+#define MDP3_REG_DMA_P_CSC_MV2 0x93440
+#define MDP3_REG_DMA_P_CSC_PRE_BV1 0x93500
+#define MDP3_REG_DMA_P_CSC_PRE_BV2 0x93540
+#define MDP3_REG_DMA_P_CSC_POST_BV1 0x93580
+#define MDP3_REG_DMA_P_CSC_POST_BV2 0x935c0
+#define MDP3_REG_DMA_P_CSC_PRE_LV1 0x93600
+#define MDP3_REG_DMA_P_CSC_PRE_LV2 0x93640
+#define MDP3_REG_DMA_P_CSC_POST_LV1 0x93680
+#define MDP3_REG_DMA_P_CSC_POST_LV2 0x936c0
+#define MDP3_REG_DMA_P_CSC_LUT1 0x93800
+#define MDP3_REG_DMA_P_CSC_LUT2 0x93c00
+#define MDP3_REG_DMA_P_HIST_START 0x94000
+#define MDP3_REG_DMA_P_HIST_FRAME_CNT 0x94004
+#define MDP3_REG_DMA_P_HIST_BIT_MASK 0x94008
+#define MDP3_REG_DMA_P_HIST_RESET_SEQ_START 0x9400c
+#define MDP3_REG_DMA_P_HIST_CONTROL 0x94010
+#define MDP3_REG_DMA_P_HIST_INTR_STATUS 0x94014
+#define MDP3_REG_DMA_P_HIST_INTR_CLEAR 0x94018
+#define MDP3_REG_DMA_P_HIST_INTR_ENABLE 0x9401c
+#define MDP3_REG_DMA_P_HIST_STOP_REQ 0x94020
+#define MDP3_REG_DMA_P_HIST_CANCEL_REQ 0x94024
+#define MDP3_REG_DMA_P_HIST_EXTRA_INFO_0 0x94028
+#define MDP3_REG_DMA_P_HIST_EXTRA_INFO_1 0x9402c
+#define MDP3_REG_DMA_P_HIST_R_DATA 0x94100
+#define MDP3_REG_DMA_P_HIST_G_DATA 0x94200
+#define MDP3_REG_DMA_P_HIST_B_DATA 0x94300
+#define MDP3_REG_DMA_P_FETCH_CFG 0x90074
+#define MDP3_REG_DMA_P_DCVS_CTRL 0x90080
+#define MDP3_REG_DMA_P_DCVS_STATUS 0x90084
+
+/*DMA_S*/
+#define MDP3_REG_DMA_S_CONFIG 0x90000
+#define MDP3_REG_DMA_S_SIZE 0x90004
+#define MDP3_REG_DMA_S_IBUF_ADDR 0x90008
+#define MDP3_REG_DMA_S_IBUF_Y_STRIDE 0x9000C
+#define MDP3_REG_DMA_S_OUT_XY 0x90010
+
+/*interface*/
+#define MDP3_REG_LCDC_EN 0xE0000
+#define MDP3_REG_LCDC_HSYNC_CTL 0xE0004
+#define MDP3_REG_LCDC_VSYNC_PERIOD 0xE0008
+#define MDP3_REG_LCDC_VSYNC_PULSE_WIDTH 0xE000C
+#define MDP3_REG_LCDC_DISPLAY_HCTL 0xE0010
+#define MDP3_REG_LCDC_DISPLAY_V_START 0xE0014
+#define MDP3_REG_LCDC_DISPLAY_V_END 0xE0018
+#define MDP3_REG_LCDC_ACTIVE_HCTL 0xE001C
+#define MDP3_REG_LCDC_ACTIVE_V_START 0xE0020
+#define MDP3_REG_LCDC_ACTIVE_V_END 0xE0024
+#define MDP3_REG_LCDC_BORDER_COLOR 0xE0028
+#define MDP3_REG_LCDC_UNDERFLOW_CTL 0xE002C
+#define MDP3_REG_LCDC_HSYNC_SKEW 0xE0030
+#define MDP3_REG_LCDC_TEST_CTL 0xE0034
+#define MDP3_REG_LCDC_CTL_POLARITY 0xE0038
+#define MDP3_REG_LCDC_TEST_COL_VAR1 0xE003C
+#define MDP3_REG_LCDC_TEST_COL_VAR2 0xE0040
+#define MDP3_REG_LCDC_UFLOW_HIDING_CTL 0xE0044
+#define MDP3_REG_LCDC_LOST_PIXEL_CNT_VALUE 0xE0048
+
+#define MDP3_REG_DSI_VIDEO_EN 0xF0000
+#define MDP3_REG_DSI_VIDEO_HSYNC_CTL 0xF0004
+#define MDP3_REG_DSI_VIDEO_VSYNC_PERIOD 0xF0008
+#define MDP3_REG_DSI_VIDEO_VSYNC_PULSE_WIDTH 0xF000C
+#define MDP3_REG_DSI_VIDEO_DISPLAY_HCTL 0xF0010
+#define MDP3_REG_DSI_VIDEO_DISPLAY_V_START 0xF0014
+#define MDP3_REG_DSI_VIDEO_DISPLAY_V_END 0xF0018
+#define MDP3_REG_DSI_VIDEO_ACTIVE_HCTL 0xF001C
+#define MDP3_REG_DSI_VIDEO_ACTIVE_V_START 0xF0020
+#define MDP3_REG_DSI_VIDEO_ACTIVE_V_END 0xF0024
+#define MDP3_REG_DSI_VIDEO_BORDER_COLOR 0xF0028
+#define MDP3_REG_DSI_VIDEO_UNDERFLOW_CTL 0xF002C
+#define MDP3_REG_DSI_VIDEO_HSYNC_SKEW 0xF0030
+#define MDP3_REG_DSI_VIDEO_TEST_CTL 0xF0034
+#define MDP3_REG_DSI_VIDEO_CTL_POLARITY 0xF0038
+#define MDP3_REG_DSI_VIDEO_TEST_COL_VAR1 0xF003C
+#define MDP3_REG_DSI_VIDEO_TEST_COL_VAR2 0xF0040
+#define MDP3_REG_DSI_VIDEO_UFLOW_HIDING_CTL 0xF0044
+#define MDP3_REG_DSI_VIDEO_LOST_PIXEL_CNT_VALUE 0xF0048
+
+#define MDP3_REG_DSI_CMD_MODE_ID_MAP 0xF1000
+#define MDP3_REG_DSI_CMD_MODE_TRIGGER_EN 0xF1004
+
+/*interrupt mask*/
+
+#define MDP3_INTR_DP0_ROI_DONE_BIT BIT(0)
+#define MDP3_INTR_DP1_ROI_DONE_BIT BIT(1)
+#define MDP3_INTR_DMA_S_DONE_BIT BIT(2)
+#define MDP3_INTR_DMA_E_DONE_BIT BIT(3)
+#define MDP3_INTR_DP0_TERMINAL_FRAME_DONE_BIT BIT(4)
+#define MDP3_INTR_DP1_TERMINAL_FRAME_DONE_BIT BIT(5)
+#define MDP3_INTR_DMA_TV_DONE_BIT BIT(6)
+#define MDP3_INTR_TV_ENCODER_UNDER_RUN_BIT BIT(7)
+#define MDP3_INTR_SYNC_PRIMARY_LINE_BIT BIT(8)
+#define MDP3_INTR_SYNC_SECONDARY_LINE_BIT BIT(9)
+#define MDP3_INTR_SYNC_EXTERNAL_LINE_BIT BIT(10)
+#define MDP3_INTR_DP0_FETCH_DONE_BIT BIT(11)
+#define MDP3_INTR_DP1_FETCH_DONE_BIT BIT(12)
+#define MDP3_INTR_TV_OUT_FRAME_START_BIT BIT(13)
+#define MDP3_INTR_DMA_P_DONE_BIT BIT(14)
+#define MDP3_INTR_LCDC_START_OF_FRAME_BIT BIT(15)
+#define MDP3_INTR_LCDC_UNDERFLOW_BIT BIT(16)
+#define MDP3_INTR_DMA_P_LINE_BIT BIT(17)
+#define MDP3_INTR_DMA_S_LINE_BIT BIT(18)
+#define MDP3_INTR_DMA_E_LINE_BIT BIT(19)
+#define MDP3_INTR_DMA_P_HISTO_BIT BIT(20)
+#define MDP3_INTR_DTV_OUT_DONE_BIT BIT(21)
+#define MDP3_INTR_DTV_OUT_START_OF_FRAME_BIT BIT(22)
+#define MDP3_INTR_DTV_OUT_UNDERFLOW_BIT BIT(23)
+#define MDP3_INTR_DTV_OUT_LINE_BIT BIT(24)
+#define MDP3_INTR_DMA_P_AUTO_FREFRESH_START_BIT BIT(25)
+#define MDP3_INTR_DMA_S_AUTO_FREFRESH_START_BIT BIT(26)
+#define MDP3_INTR_QPIC_EOF_ENABLE_BIT BIT(27)
+
+enum {
+ MDP3_INTR_DP0_ROI_DONE,
+ MDP3_INTR_DP1_ROI_DONE,
+ MDP3_INTR_DMA_S_DONE,
+ MDP3_INTR_DMA_E_DONE,
+ MDP3_INTR_DP0_TERMINAL_FRAME_DONE,
+ MDP3_INTR_DP1_TERMINAL_FRAME_DONE,
+ MDP3_INTR_DMA_TV_DONE,
+ MDP3_INTR_TV_ENCODER_UNDER_RUN,
+ MDP3_INTR_SYNC_PRIMARY_LINE,
+ MDP3_INTR_SYNC_SECONDARY_LINE,
+ MDP3_INTR_SYNC_EXTERNAL_LINE,
+ MDP3_INTR_DP0_FETCH_DONE,
+ MDP3_INTR_DP1_FETCH_DONE,
+ MDP3_INTR_TV_OUT_FRAME_START,
+ MDP3_INTR_DMA_P_DONE,
+ MDP3_INTR_LCDC_START_OF_FRAME,
+ MDP3_INTR_LCDC_UNDERFLOW,
+ MDP3_INTR_DMA_P_LINE,
+ MDP3_INTR_DMA_S_LINE,
+ MDP3_INTR_DMA_E_LINE,
+ MDP3_INTR_DMA_P_HISTO,
+ MDP3_INTR_DTV_OUT_DONE,
+ MDP3_INTR_DTV_OUT_START_OF_FRAME,
+ MDP3_INTR_DTV_OUT_UNDERFLOW,
+ MDP3_INTR_DTV_OUT_LINE,
+ MDP3_INTR_DMA_P_AUTO_FREFRESH_START,
+ MDP3_INTR_DMA_S_AUTO_FREFRESH_START,
+ MDP3_INTR_QPIC_EOF_ENABLE,
+};
+
+#define MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT BIT(0)
+#define MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT BIT(1)
+
+#endif /* MDP3_HWIO_H */