summaryrefslogtreecommitdiff
path: root/drivers/gpu/imx/dpu/dpu-extdst.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/imx/dpu/dpu-extdst.c')
-rw-r--r--drivers/gpu/imx/dpu/dpu-extdst.c503
1 files changed, 503 insertions, 0 deletions
diff --git a/drivers/gpu/imx/dpu/dpu-extdst.c b/drivers/gpu/imx/dpu/dpu-extdst.c
new file mode 100644
index 000000000000..ab79fe6a02c8
--- /dev/null
+++ b/drivers/gpu/imx/dpu/dpu-extdst.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <video/dpu.h>
+#include "dpu-prv.h"
+
+#define PIXENGCFG_STATIC 0x8
+#define POWERDOWN BIT(4)
+#define SYNC_MODE BIT(8)
+#define SW_RESET BIT(11)
+#define DIV(n) (((n) & 0xFF) << 16)
+#define DIV_RESET 0x80
+#define PIXENGCFG_DYNAMIC 0xC
+#define PIXENGCFG_REQUEST 0x10
+#define SHDLDREQ(n) BIT(n)
+#define SEL_SHDLDREQ BIT(0)
+#define PIXENGCFG_TRIGGER 0x14
+#define SYNC_TRIGGER BIT(0)
+#define TRIGGER_SEQUENCE_COMPLETE BIT(4)
+#define PIXENGCFG_STATUS 0x18
+#define SYNC_BUSY BIT(8)
+#define KICK_MODE BIT(8)
+#define PERFCOUNTMODE BIT(12)
+#define CONTROL 0xC
+#define GAMMAAPPLYENABLE BIT(0)
+#define SOFTWAREKICK 0x10
+#define KICK BIT(0)
+#define STATUS 0x14
+#define CNT_ERR_STS BIT(0)
+#define CONTROLWORD 0x18
+#define CURPIXELCNT 0x1C
+static u16 get_xval(u32 pixel_cnt)
+{
+ return pixel_cnt && 0xFF;
+}
+
+static u16 get_yval(u32 pixel_cnt)
+{
+ return pixel_cnt >> 16;
+}
+#define LASTPIXELCNT 0x20
+#define PERFCOUNTER 0x24
+
+struct dpu_extdst {
+ void __iomem *pec_base;
+ void __iomem *base;
+ struct mutex mutex;
+ int id;
+ bool inuse;
+ struct dpu_soc *dpu;
+};
+
+static inline u32 dpu_pec_ed_read(struct dpu_extdst *ed, unsigned int offset)
+{
+ return readl(ed->pec_base + offset);
+}
+
+static inline void dpu_pec_ed_write(struct dpu_extdst *ed, u32 value,
+ unsigned int offset)
+{
+ writel(value, ed->pec_base + offset);
+}
+
+static inline u32 dpu_ed_read(struct dpu_extdst *ed, unsigned int offset)
+{
+ return readl(ed->base + offset);
+}
+
+static inline void dpu_ed_write(struct dpu_extdst *ed, u32 value,
+ unsigned int offset)
+{
+ writel(value, ed->base + offset);
+}
+
+static inline bool dpu_ed_is_safety_stream(struct dpu_extdst *ed)
+{
+ if (ed->id == 4 || ed->id == 5)
+ return true;
+
+ return false;
+}
+
+static inline bool dpu_ed_src_sel_is_extsrc(extdst_src_sel_t src)
+{
+ if (src == ED_SRC_EXTSRC4 || src == ED_SRC_EXTSRC5)
+ return true;
+
+ return false;
+}
+
+void extdst_pixengcfg_shden(struct dpu_extdst *ed, bool enable)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATIC);
+ if (enable)
+ val |= SHDEN;
+ else
+ val &= ~SHDEN;
+ dpu_pec_ed_write(ed, val, PIXENGCFG_STATIC);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_shden);
+
+void extdst_pixengcfg_powerdown(struct dpu_extdst *ed, bool powerdown)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATIC);
+ if (powerdown)
+ val |= POWERDOWN;
+ else
+ val &= ~POWERDOWN;
+ dpu_pec_ed_write(ed, val, PIXENGCFG_STATIC);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_powerdown);
+
+void extdst_pixengcfg_sync_mode(struct dpu_extdst *ed, ed_sync_mode_t mode)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATIC);
+ if (mode == AUTO)
+ val |= SYNC_MODE;
+ else
+ val &= ~SYNC_MODE;
+ dpu_pec_ed_write(ed, val, PIXENGCFG_STATIC);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_sync_mode);
+
+void extdst_pixengcfg_reset(struct dpu_extdst *ed, bool reset)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATIC);
+ if (reset)
+ val |= SW_RESET;
+ else
+ val &= ~SW_RESET;
+ dpu_pec_ed_write(ed, val, PIXENGCFG_STATIC);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_reset);
+
+void extdst_pixengcfg_div(struct dpu_extdst *ed, u16 div)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATIC);
+ val &= ~0xFF0000;
+ val |= DIV(div);
+ dpu_pec_ed_write(ed, val, PIXENGCFG_STATIC);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_div);
+
+int extdst_pixengcfg_src_sel(struct dpu_extdst *ed, extdst_src_sel_t src)
+{
+ struct dpu_soc *dpu = ed->dpu;
+ const unsigned int *block_id_map = dpu->devtype->sw2hw_block_id_map;
+ u32 mapped_src;
+
+ mapped_src = block_id_map ? block_id_map[src] : src;
+ if (WARN_ON(mapped_src == NA))
+ return -EINVAL;
+
+ if (dpu_ed_is_safety_stream(ed) && dpu_ed_src_sel_is_extsrc(src)) {
+ dev_err(dpu->dev, "ExtDst%d source cannot be ExtSrc\n", ed->id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ed->mutex);
+ dpu_pec_ed_write(ed, mapped_src, PIXENGCFG_DYNAMIC);
+ mutex_unlock(&ed->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_src_sel);
+
+void extdst_pixengcfg_sel_shdldreq(struct dpu_extdst *ed)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_REQUEST);
+ val |= SEL_SHDLDREQ;
+ dpu_pec_ed_write(ed, val, PIXENGCFG_REQUEST);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_sel_shdldreq);
+
+void extdst_pixengcfg_shdldreq(struct dpu_extdst *ed, u32 req_mask)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_REQUEST);
+ val |= req_mask;
+ dpu_pec_ed_write(ed, val, PIXENGCFG_REQUEST);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_shdldreq);
+
+void extdst_pixengcfg_sync_trigger(struct dpu_extdst *ed)
+{
+ mutex_lock(&ed->mutex);
+ dpu_pec_ed_write(ed, SYNC_TRIGGER, PIXENGCFG_TRIGGER);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_sync_trigger);
+
+void extdst_pixengcfg_trigger_sequence_complete(struct dpu_extdst *ed)
+{
+ mutex_lock(&ed->mutex);
+ dpu_pec_ed_write(ed, TRIGGER_SEQUENCE_COMPLETE, PIXENGCFG_TRIGGER);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_trigger_sequence_complete);
+
+bool extdst_pixengcfg_is_sync_busy(struct dpu_extdst *ed)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATUS);
+ mutex_unlock(&ed->mutex);
+
+ return val & SYNC_BUSY;
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_is_sync_busy);
+
+ed_pipeline_status_t extdst_pixengcfg_pipeline_status(struct dpu_extdst *ed)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_pec_ed_read(ed, PIXENGCFG_STATUS);
+ mutex_unlock(&ed->mutex);
+
+ return val & 0x3;
+}
+EXPORT_SYMBOL_GPL(extdst_pixengcfg_pipeline_status);
+
+void extdst_shden(struct dpu_extdst *ed, bool enable)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, STATICCONTROL);
+ if (enable)
+ val |= SHDEN;
+ else
+ val &= ~SHDEN;
+ dpu_ed_write(ed, val, STATICCONTROL);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_shden);
+
+void extdst_kick_mode(struct dpu_extdst *ed, ed_kick_mode_t mode)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, STATICCONTROL);
+ val &= ~KICK_MODE;
+ val |= mode;
+ dpu_ed_write(ed, val, STATICCONTROL);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_kick_mode);
+
+void extdst_perfcountmode(struct dpu_extdst *ed, bool enable)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, STATICCONTROL);
+ if (enable)
+ val |= PERFCOUNTMODE;
+ else
+ val &= ~PERFCOUNTMODE;
+ dpu_ed_write(ed, val, STATICCONTROL);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_perfcountmode);
+
+void extdst_gamma_apply_enable(struct dpu_extdst *ed, bool enable)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, CONTROL);
+ if (enable)
+ val |= GAMMAAPPLYENABLE;
+ else
+ val &= ~GAMMAAPPLYENABLE;
+ dpu_ed_write(ed, val, CONTROL);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_gamma_apply_enable);
+
+void extdst_kick(struct dpu_extdst *ed)
+{
+ mutex_lock(&ed->mutex);
+ dpu_ed_write(ed, KICK, SOFTWAREKICK);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_kick);
+
+void extdst_cnt_err_clear(struct dpu_extdst *ed)
+{
+ mutex_lock(&ed->mutex);
+ dpu_ed_write(ed, CNT_ERR_STS, STATUS);
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(extdst_cnt_err_clear);
+
+bool extdst_cnt_err_status(struct dpu_extdst *ed)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, STATUS);
+ mutex_unlock(&ed->mutex);
+
+ return val & CNT_ERR_STS;
+}
+EXPORT_SYMBOL_GPL(extdst_cnt_err_status);
+
+u32 extdst_last_control_word(struct dpu_extdst *ed)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, CONTROLWORD);
+ mutex_unlock(&ed->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(extdst_last_control_word);
+
+void extdst_pixel_cnt(struct dpu_extdst *ed, u16 *x, u16 *y)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, CURPIXELCNT);
+ mutex_unlock(&ed->mutex);
+
+ *x = get_xval(val);
+ *y = get_yval(val);
+}
+EXPORT_SYMBOL_GPL(extdst_pixel_cnt);
+
+void extdst_last_pixel_cnt(struct dpu_extdst *ed, u16 *x, u16 *y)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, LASTPIXELCNT);
+ mutex_unlock(&ed->mutex);
+
+ *x = get_xval(val);
+ *y = get_yval(val);
+}
+EXPORT_SYMBOL_GPL(extdst_last_pixel_cnt);
+
+u32 extdst_perfresult(struct dpu_extdst *ed)
+{
+ u32 val;
+
+ mutex_lock(&ed->mutex);
+ val = dpu_ed_read(ed, PERFCOUNTER);
+ mutex_unlock(&ed->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(extdst_perfresult);
+
+struct dpu_extdst *dpu_ed_get(struct dpu_soc *dpu, int id)
+{
+ struct dpu_extdst *ed;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ed_ids); i++)
+ if (ed_ids[i] == id)
+ break;
+
+ if (i == ARRAY_SIZE(ed_ids))
+ return ERR_PTR(-EINVAL);
+
+ ed = dpu->ed_priv[i];
+
+ mutex_lock(&ed->mutex);
+
+ if (ed->inuse) {
+ ed = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ ed->inuse = true;
+out:
+ mutex_unlock(&ed->mutex);
+
+ return ed;
+}
+EXPORT_SYMBOL_GPL(dpu_ed_get);
+
+void dpu_ed_put(struct dpu_extdst *ed)
+{
+ mutex_lock(&ed->mutex);
+
+ ed->inuse = false;
+
+ mutex_unlock(&ed->mutex);
+}
+EXPORT_SYMBOL_GPL(dpu_ed_put);
+
+void _dpu_ed_init(struct dpu_soc *dpu, unsigned int id)
+{
+ struct dpu_extdst *ed;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ed_ids); i++)
+ if (ed_ids[i] == id)
+ break;
+
+ if (WARN_ON(i == ARRAY_SIZE(ed_ids)))
+ return;
+
+ ed = dpu->ed_priv[i];
+
+ extdst_pixengcfg_src_sel(ed, ED_SRC_DISABLE);
+ extdst_pixengcfg_shden(ed, true);
+ extdst_pixengcfg_powerdown(ed, false);
+ extdst_pixengcfg_sync_mode(ed, SINGLE);
+ extdst_pixengcfg_reset(ed, false);
+ extdst_pixengcfg_div(ed, DIV_RESET);
+ extdst_shden(ed, true);
+ extdst_perfcountmode(ed, false);
+ extdst_kick_mode(ed, EXTERNAL);
+}
+
+int dpu_ed_init(struct dpu_soc *dpu, unsigned int id,
+ unsigned long pec_base, unsigned long base)
+{
+ struct dpu_extdst *ed;
+ int ret, i;
+
+ ed = devm_kzalloc(dpu->dev, sizeof(*ed), GFP_KERNEL);
+ if (!ed)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(ed_ids); i++)
+ if (ed_ids[i] == id)
+ break;
+
+ dpu->ed_priv[i] = ed;
+
+ ed->pec_base = devm_ioremap(dpu->dev, pec_base, SZ_32);
+ if (!ed->pec_base)
+ return -ENOMEM;
+
+ ed->base = devm_ioremap(dpu->dev, base, SZ_64);
+ if (!ed->base)
+ return -ENOMEM;
+
+ ed->dpu = dpu;
+ ed->id = id;
+ mutex_init(&ed->mutex);
+
+ ret = extdst_pixengcfg_src_sel(ed, ED_SRC_DISABLE);
+ if (ret < 0)
+ return ret;
+
+ _dpu_ed_init(dpu, id);
+
+ return 0;
+}