On Tue, Apr 26, 2016 at 10:05:22AM +0200, Philipp Zabel wrote:
From 24982961a7406c9c6ed139329c9ee1263ddc92c2 Mon Sep 17 00:00:00 2001 From: CK Hu ck.hu@mediatek.com Date: Mon, 4 Jan 2016 18:36:34 +0100 Subject: [PATCH v14.5 2/8] drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
This patch adds an initial DRM driver for the Mediatek MT8173 DISP subsystem. It currently supports two fixed output streams from the OVL0/OVL1 sources to the DSI0/DPI0 sinks, respectively.
Signed-off-by: CK Hu ck.hu@mediatek.com Signed-off-by: YT Shen yt.shen@mediatek.com Signed-off-by: Daniel Kurtz djkurtz@chromium.org Signed-off-by: Bibby Hsieh bibby.hsieh@mediatek.com Signed-off-by: Mao Huang littlecvr@chromium.org Signed-off-by: Philipp Zabel p.zabel@pengutronix.de
Changes since v14:
- Fixed module build
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/mediatek/Kconfig | 12 + drivers/gpu/drm/mediatek/Makefile | 11 + drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 302 +++++++++++++++ drivers/gpu/drm/mediatek/mtk_disp_rdma.c | 240 ++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 582 ++++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_crtc.h | 32 ++ drivers/gpu/drm/mediatek/mtk_drm_ddp.c | 353 +++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_ddp.h | 41 ++ drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 225 +++++++++++ drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 150 +++++++ drivers/gpu/drm/mediatek/mtk_drm_drv.c | 565 +++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_drv.h | 57 +++ drivers/gpu/drm/mediatek/mtk_drm_fb.c | 165 ++++++++ drivers/gpu/drm/mediatek/mtk_drm_fb.h | 23 ++ drivers/gpu/drm/mediatek/mtk_drm_gem.c | 269 +++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_gem.h | 59 +++ drivers/gpu/drm/mediatek/mtk_drm_plane.c | 240 ++++++++++++ drivers/gpu/drm/mediatek/mtk_drm_plane.h | 59 +++ 20 files changed, 3388 insertions(+) create mode 100644 drivers/gpu/drm/mediatek/Kconfig create mode 100644 drivers/gpu/drm/mediatek/Makefile create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_ovl.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_rdma.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_fb.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f2a74d0..5482012 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -281,3 +281,5 @@ source "drivers/gpu/drm/imx/Kconfig" source "drivers/gpu/drm/vc4/Kconfig"
source "drivers/gpu/drm/etnaviv/Kconfig"
+source "drivers/gpu/drm/mediatek/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6eb94fc..02b1f3e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ obj-$(CONFIG_DRM_IMX) += imx/ +obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig new file mode 100644 index 0000000..8dad892 --- /dev/null +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -0,0 +1,12 @@ +config DRM_MEDIATEK
- tristate "DRM Support for Mediatek SoCs"
- depends on DRM
- depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
- select DRM_KMS_HELPER
- select IOMMU_DMA
- select MTK_SMI
- help
Choose this option if you have a Mediatek SoCs.
The module will be called mediatek-drm
This driver provides kernel mode setting and
buffer management to userspace.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile new file mode 100644 index 0000000..d4bde7c --- /dev/null +++ b/drivers/gpu/drm/mediatek/Makefile @@ -0,0 +1,11 @@ +mediatek-drm-y := mtk_disp_ovl.o \
mtk_disp_rdma.o \
mtk_drm_crtc.o \
mtk_drm_ddp.o \
mtk_drm_ddp_comp.o \
mtk_drm_drv.o \
mtk_drm_fb.o \
mtk_drm_gem.o \
mtk_drm_plane.o
+obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c new file mode 100644 index 0000000..8f62671f --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -0,0 +1,302 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 <drm/drmP.h> +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h>
+#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h"
+#define DISP_REG_OVL_INTEN 0x0004 +#define OVL_FME_CPL_INT BIT(1) +#define DISP_REG_OVL_INTSTA 0x0008 +#define DISP_REG_OVL_EN 0x000c +#define DISP_REG_OVL_RST 0x0014 +#define DISP_REG_OVL_ROI_SIZE 0x0020 +#define DISP_REG_OVL_ROI_BGCLR 0x0028 +#define DISP_REG_OVL_SRC_CON 0x002c +#define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n)) +#define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n)) +#define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n)) +#define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n)) +#define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n)) +#define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n)) +#define DISP_REG_OVL_ADDR(n) (0x0f40 + 0x20 * (n))
+#define OVL_RDMA_MEM_GMC 0x40402020
+#define OVL_CON_BYTE_SWAP BIT(24) +#define OVL_CON_CLRFMT_RGB565 (0 << 12) +#define OVL_CON_CLRFMT_RGB888 (1 << 12) +#define OVL_CON_CLRFMT_RGBA8888 (2 << 12) +#define OVL_CON_CLRFMT_ARGB8888 (3 << 12) +#define OVL_CON_AEN BIT(8) +#define OVL_CON_ALPHA 0xff
+/**
- struct mtk_disp_ovl - DISP_OVL driver structure
- @ddp_comp - structure containing type enum and hardware resources
- @crtc - associated crtc to report vblank events to
- */
+struct mtk_disp_ovl {
- struct mtk_ddp_comp ddp_comp;
- struct drm_crtc *crtc;
+};
+static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id) +{
- struct mtk_disp_ovl *priv = dev_id;
- struct mtk_ddp_comp *ovl = &priv->ddp_comp;
- /* Clear frame completion interrupt */
- writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
- if (!priv->crtc)
return IRQ_NONE;
- mtk_crtc_ddp_irq(priv->crtc, ovl);
- return IRQ_HANDLED;
+}
+static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
struct drm_crtc *crtc)
+{
- struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
ddp_comp);
- priv->crtc = crtc;
- writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
+}
+static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp) +{
- struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
ddp_comp);
- priv->crtc = NULL;
- writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN);
+}
+static void mtk_ovl_start(struct mtk_ddp_comp *comp) +{
- writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
+}
+static void mtk_ovl_stop(struct mtk_ddp_comp *comp) +{
- writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
+}
+static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
unsigned int h, unsigned int vrefresh)
+{
- if (w != 0 && h != 0)
writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
- writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR);
- writel(0x1, comp->regs + DISP_REG_OVL_RST);
- writel(0x0, comp->regs + DISP_REG_OVL_RST);
+}
+static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx) +{
- unsigned int reg;
- writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
- writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
- reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
- reg = reg | BIT(idx);
- writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
+}
+static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx) +{
- unsigned int reg;
- reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
- reg = reg & ~BIT(idx);
- writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
- writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
+}
+static unsigned int ovl_fmt_convert(unsigned int fmt) +{
- switch (fmt) {
- default:
- case DRM_FORMAT_RGB565:
return OVL_CON_CLRFMT_RGB565;
- case DRM_FORMAT_BGR565:
return OVL_CON_CLRFMT_RGB565 | OVL_CON_BYTE_SWAP;
- case DRM_FORMAT_RGB888:
return OVL_CON_CLRFMT_RGB888;
- case DRM_FORMAT_BGR888:
return OVL_CON_CLRFMT_RGB888 | OVL_CON_BYTE_SWAP;
- case DRM_FORMAT_RGBX8888:
- case DRM_FORMAT_RGBA8888:
return OVL_CON_CLRFMT_ARGB8888;
- case DRM_FORMAT_BGRX8888:
- case DRM_FORMAT_BGRA8888:
return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_ARGB8888:
return OVL_CON_CLRFMT_RGBA8888;
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_ABGR8888:
return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
- }
+}
+static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
struct mtk_plane_state *state)
+{
- struct mtk_plane_pending_state *pending = &state->pending;
- unsigned int addr = pending->addr;
- unsigned int pitch = pending->pitch & 0xffff;
- unsigned int fmt = pending->format;
- unsigned int offset = (pending->y << 16) | pending->x;
- unsigned int src_size = (pending->height << 16) | pending->width;
- unsigned int con;
- if (!pending->enable)
mtk_ovl_layer_off(comp, idx);
- con = ovl_fmt_convert(fmt);
- if (idx != 0)
con |= OVL_CON_AEN | OVL_CON_ALPHA;
- writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
- writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
- writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
- writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
- writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(idx));
- if (pending->enable)
mtk_ovl_layer_on(comp, idx);
+}
+static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
- .config = mtk_ovl_config,
- .start = mtk_ovl_start,
- .stop = mtk_ovl_stop,
- .enable_vblank = mtk_ovl_enable_vblank,
- .disable_vblank = mtk_ovl_disable_vblank,
- .layer_on = mtk_ovl_layer_on,
- .layer_off = mtk_ovl_layer_off,
- .layer_config = mtk_ovl_layer_config,
+};
+static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
void *data)
+{
- struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
- struct drm_device *drm_dev = data;
- int ret;
- ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
- if (ret < 0) {
dev_err(dev, "Failed to register component %s: %d\n",
dev->of_node->full_name, ret);
return ret;
- }
- return 0;
+}
+static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
void *data)
+{
- struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
- struct drm_device *drm_dev = data;
- mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
+}
+static const struct component_ops mtk_disp_ovl_component_ops = {
- .bind = mtk_disp_ovl_bind,
- .unbind = mtk_disp_ovl_unbind,
+};
+static int mtk_disp_ovl_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct mtk_disp_ovl *priv;
- int comp_id;
- int irq;
- int ret;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
return -ENOMEM;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
return irq;
- ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
IRQF_TRIGGER_NONE, dev_name(dev), priv);
- if (ret < 0) {
dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
return ret;
- }
- comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
- if (comp_id < 0) {
dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
return comp_id;
- }
- ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
&mtk_disp_ovl_funcs);
- if (ret) {
dev_err(dev, "Failed to initialize component: %d\n", ret);
return ret;
- }
- platform_set_drvdata(pdev, priv);
- ret = component_add(dev, &mtk_disp_ovl_component_ops);
- if (ret)
dev_err(dev, "Failed to add component: %d\n", ret);
- return ret;
+}
+static int mtk_disp_ovl_remove(struct platform_device *pdev) +{
- component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
- return 0;
+}
+static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
- { .compatible = "mediatek,mt8173-disp-ovl", },
- {},
+}; +MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
+struct platform_driver mtk_disp_ovl_driver = {
- .probe = mtk_disp_ovl_probe,
- .remove = mtk_disp_ovl_remove,
- .driver = {
.name = "mediatek-disp-ovl",
.owner = THIS_MODULE,
.of_match_table = mtk_disp_ovl_driver_dt_match,
- },
+}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c new file mode 100644 index 0000000..5fb80cb --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -0,0 +1,240 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 <drm/drmP.h> +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h>
+#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h"
+#define DISP_REG_RDMA_INT_ENABLE 0x0000 +#define DISP_REG_RDMA_INT_STATUS 0x0004 +#define RDMA_TARGET_LINE_INT BIT(5) +#define RDMA_FIFO_UNDERFLOW_INT BIT(4) +#define RDMA_EOF_ABNORMAL_INT BIT(3) +#define RDMA_FRAME_END_INT BIT(2) +#define RDMA_FRAME_START_INT BIT(1) +#define RDMA_REG_UPDATE_INT BIT(0) +#define DISP_REG_RDMA_GLOBAL_CON 0x0010 +#define RDMA_ENGINE_EN BIT(0) +#define DISP_REG_RDMA_SIZE_CON_0 0x0014 +#define DISP_REG_RDMA_SIZE_CON_1 0x0018 +#define DISP_REG_RDMA_TARGET_LINE 0x001c +#define DISP_REG_RDMA_FIFO_CON 0x0040 +#define RDMA_FIFO_UNDERFLOW_EN BIT(31) +#define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16) +#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16)
+/**
- struct mtk_disp_rdma - DISP_RDMA driver structure
- @ddp_comp - structure containing type enum and hardware resources
- @crtc - associated crtc to report irq events to
- */
+struct mtk_disp_rdma {
- struct mtk_ddp_comp ddp_comp;
- struct drm_crtc *crtc;
+};
+static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id) +{
- struct mtk_disp_rdma *priv = dev_id;
- struct mtk_ddp_comp *rdma = &priv->ddp_comp;
- /* Clear frame completion interrupt */
- writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS);
- if (!priv->crtc)
return IRQ_NONE;
- mtk_crtc_ddp_irq(priv->crtc, rdma);
- return IRQ_HANDLED;
+}
+static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg,
unsigned int mask, unsigned int val)
+{
- unsigned int tmp = readl(comp->regs + reg);
- tmp = (tmp & ~mask) | (val & mask);
- writel(tmp, comp->regs + reg);
+}
+static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp,
struct drm_crtc *crtc)
+{
- struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
ddp_comp);
- priv->crtc = crtc;
- rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
RDMA_FRAME_END_INT);
+}
+static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp) +{
- struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
ddp_comp);
- priv->crtc = NULL;
- rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
+}
+static void mtk_rdma_start(struct mtk_ddp_comp *comp) +{
- rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
RDMA_ENGINE_EN);
+}
+static void mtk_rdma_stop(struct mtk_ddp_comp *comp) +{
- rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
+}
+static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
unsigned int height, unsigned int vrefresh)
+{
- unsigned int threshold;
- unsigned int reg;
- rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width);
- rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height);
- /*
* Enable FIFO underflow since DSI and DPI can't be blocked.
* Keep the FIFO pseudo size reset default of 8 KiB. Set the
* output threshold to 6 microseconds with 7/6 overhead to
* account for blanking, and with a pixel depth of 4 bytes:
*/
- threshold = width * height * vrefresh * 4 * 7 / 1000000;
- reg = RDMA_FIFO_UNDERFLOW_EN |
RDMA_FIFO_PSEUDO_SIZE(SZ_8K) |
RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
- writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
+}
+static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = {
- .config = mtk_rdma_config,
- .start = mtk_rdma_start,
- .stop = mtk_rdma_stop,
- .enable_vblank = mtk_rdma_enable_vblank,
- .disable_vblank = mtk_rdma_disable_vblank,
+};
+static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
void *data)
+{
- struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
- struct drm_device *drm_dev = data;
- int ret;
- ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
- if (ret < 0) {
dev_err(dev, "Failed to register component %s: %d\n",
dev->of_node->full_name, ret);
return ret;
- }
- return 0;
+}
+static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
void *data)
+{
- struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
- struct drm_device *drm_dev = data;
- mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
+}
+static const struct component_ops mtk_disp_rdma_component_ops = {
- .bind = mtk_disp_rdma_bind,
- .unbind = mtk_disp_rdma_unbind,
+};
+static int mtk_disp_rdma_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct mtk_disp_rdma *priv;
- int comp_id;
- int irq;
- int ret;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
return -ENOMEM;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
return irq;
- comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA);
- if (comp_id < 0) {
dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
return comp_id;
- }
- ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
&mtk_disp_rdma_funcs);
- if (ret) {
dev_err(dev, "Failed to initialize component: %d\n", ret);
return ret;
- }
- /* Disable and clear pending interrupts */
- writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE);
- writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS);
- ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
IRQF_TRIGGER_NONE, dev_name(dev), priv);
- if (ret < 0) {
dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
return ret;
- }
- platform_set_drvdata(pdev, priv);
- ret = component_add(dev, &mtk_disp_rdma_component_ops);
- if (ret)
dev_err(dev, "Failed to add component: %d\n", ret);
- return ret;
+}
+static int mtk_disp_rdma_remove(struct platform_device *pdev) +{
- component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
- return 0;
+}
+static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
- { .compatible = "mediatek,mt8173-disp-rdma", },
- {},
+}; +MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
+struct platform_driver mtk_disp_rdma_driver = {
- .probe = mtk_disp_rdma_probe,
- .remove = mtk_disp_rdma_remove,
- .driver = {
.name = "mediatek-disp-rdma",
.owner = THIS_MODULE,
.of_match_table = mtk_disp_rdma_driver_dt_match,
- },
+}; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c new file mode 100644 index 0000000..f8cb6c6 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -0,0 +1,582 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 <asm/barrier.h> +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <soc/mediatek/smi.h>
+#include "mtk_drm_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp.h" +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_gem.h" +#include "mtk_drm_plane.h"
+/**
- struct mtk_drm_crtc - MediaTek specific crtc structure.
- @base: crtc object.
- @enabled: records whether crtc_enable succeeded
- @planes: array of 4 mtk_drm_plane structures, one for each overlay plane
- @pending_planes: whether any plane has pending changes to be applied
- @config_regs: memory mapped mmsys configuration register space
- @mutex: handle to one of the ten disp_mutex streams
- @ddp_comp_nr: number of components in ddp_comp
- @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
- */
+struct mtk_drm_crtc {
- struct drm_crtc base;
- bool enabled;
- bool pending_needs_vblank;
- struct drm_pending_vblank_event *event;
- struct mtk_drm_plane planes[OVL_LAYER_NR];
- bool pending_planes;
- void __iomem *config_regs;
- struct mtk_disp_mutex *mutex;
- unsigned int ddp_comp_nr;
- struct mtk_ddp_comp **ddp_comp;
+};
+struct mtk_crtc_state {
- struct drm_crtc_state base;
- bool pending_config;
- unsigned int pending_width;
- unsigned int pending_height;
- unsigned int pending_vrefresh;
+};
+static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c) +{
- return container_of(c, struct mtk_drm_crtc, base);
+}
+static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s) +{
- return container_of(s, struct mtk_crtc_state, base);
+}
+static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) +{
- struct drm_crtc *crtc = &mtk_crtc->base;
- unsigned long flags;
- spin_lock_irqsave(&crtc->dev->event_lock, flags);
- drm_send_vblank_event(crtc->dev, mtk_crtc->event->pipe, mtk_crtc->event);
- drm_crtc_vblank_put(crtc);
- mtk_crtc->event = NULL;
- spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+}
+static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) +{
- drm_crtc_handle_vblank(&mtk_crtc->base);
- if (mtk_crtc->pending_needs_vblank) {
mtk_drm_crtc_finish_page_flip(mtk_crtc);
mtk_crtc->pending_needs_vblank = false;
- }
+}
+static void mtk_drm_crtc_destroy(struct drm_crtc *crtc) +{
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
- int i;
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
- mtk_disp_mutex_put(mtk_crtc->mutex);
- drm_crtc_cleanup(crtc);
+}
+static void mtk_drm_crtc_reset(struct drm_crtc *crtc) +{
- struct mtk_crtc_state *state;
- if (crtc->state) {
if (crtc->state->mode_blob)
drm_property_unreference_blob(crtc->state->mode_blob);
state = to_mtk_crtc_state(crtc->state);
memset(state, 0, sizeof(*state));
- } else {
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return;
crtc->state = &state->base;
- }
- state->base.crtc = crtc;
+}
+static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc) +{
- struct mtk_crtc_state *state;
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
return NULL;
- __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
- WARN_ON(state->base.crtc != crtc);
- state->base.crtc = crtc;
- return &state->base;
+}
+static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
+{
- __drm_atomic_helper_crtc_destroy_state(crtc, state);
- kfree(to_mtk_crtc_state(state));
+}
+static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- /* Nothing to do here, but this callback is mandatory. */
- return true;
+}
+static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) +{
- struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
- state->pending_width = crtc->mode.hdisplay;
- state->pending_height = crtc->mode.vdisplay;
- state->pending_vrefresh = crtc->mode.vrefresh;
- wmb(); /* Make sure the above parameters are set before update */
- state->pending_config = true;
+}
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe) +{
- struct mtk_drm_private *priv = drm->dev_private;
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
- struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
- mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base);
- return 0;
+}
+void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe) +{
- struct mtk_drm_private *priv = drm->dev_private;
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
- struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
- mtk_ddp_comp_disable_vblank(ovl);
+}
+static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc) +{
- int ret;
- int i;
- DRM_DEBUG_DRIVER("%s\n", __func__);
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
ret = clk_enable(mtk_crtc->ddp_comp[i]->clk);
if (ret) {
DRM_ERROR("Failed to enable clock %d: %d\n", i, ret);
goto err;
}
- }
- return 0;
+err:
- while (--i >= 0)
clk_disable(mtk_crtc->ddp_comp[i]->clk);
- return ret;
+}
+static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc) +{
- int i;
- DRM_DEBUG_DRIVER("%s\n", __func__);
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
clk_disable(mtk_crtc->ddp_comp[i]->clk);
+}
+static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) +{
- struct drm_crtc *crtc = &mtk_crtc->base;
- unsigned int width, height, vrefresh;
- int ret;
- int i;
- DRM_DEBUG_DRIVER("%s\n", __func__);
- if (WARN_ON(!crtc->state))
return -EINVAL;
- width = crtc->state->adjusted_mode.hdisplay;
- height = crtc->state->adjusted_mode.vdisplay;
- vrefresh = crtc->state->adjusted_mode.vrefresh;
- ret = pm_runtime_get_sync(crtc->dev->dev);
- if (ret < 0) {
DRM_ERROR("Failed to enable power domain: %d\n", ret);
return ret;
- }
- ret = mtk_disp_mutex_prepare(mtk_crtc->mutex);
- if (ret < 0) {
DRM_ERROR("Failed to enable mutex clock: %d\n", ret);
goto err_pm_runtime_put;
- }
- ret = mtk_crtc_ddp_clk_enable(mtk_crtc);
- if (ret < 0) {
DRM_ERROR("Failed to enable component clocks: %d\n", ret);
goto err_mutex_unprepare;
- }
- DRM_DEBUG_DRIVER("mediatek_ddp_ddp_path_setup\n");
- for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
mtk_ddp_add_comp_to_path(mtk_crtc->config_regs,
mtk_crtc->ddp_comp[i]->id,
mtk_crtc->ddp_comp[i + 1]->id);
mtk_disp_mutex_add_comp(mtk_crtc->mutex,
mtk_crtc->ddp_comp[i]->id);
- }
- mtk_disp_mutex_add_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
- mtk_disp_mutex_enable(mtk_crtc->mutex);
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
mtk_ddp_comp_config(comp, width, height, vrefresh);
mtk_ddp_comp_start(comp);
- }
- /* Initially configure all planes */
- for (i = 0; i < OVL_LAYER_NR; i++) {
struct drm_plane *plane = &mtk_crtc->planes[i].base;
struct mtk_plane_state *plane_state;
plane_state = to_mtk_plane_state(plane->state);
mtk_ddp_comp_layer_config(mtk_crtc->ddp_comp[0], i,
plane_state);
- }
- return 0;
+err_mutex_unprepare:
- mtk_disp_mutex_unprepare(mtk_crtc->mutex);
+err_pm_runtime_put:
- pm_runtime_put(crtc->dev->dev);
- return ret;
+}
+static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc) +{
- struct drm_device *drm = mtk_crtc->base.dev;
- int i;
- DRM_DEBUG_DRIVER("%s\n", __func__);
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]);
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
mtk_crtc->ddp_comp[i]->id);
- mtk_disp_mutex_disable(mtk_crtc->mutex);
- for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
mtk_ddp_remove_comp_from_path(mtk_crtc->config_regs,
mtk_crtc->ddp_comp[i]->id,
mtk_crtc->ddp_comp[i + 1]->id);
mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
mtk_crtc->ddp_comp[i]->id);
- }
- mtk_disp_mutex_remove_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
- mtk_crtc_ddp_clk_disable(mtk_crtc);
- mtk_disp_mutex_unprepare(mtk_crtc->mutex);
- pm_runtime_put(drm->dev);
+}
+static void mtk_drm_crtc_enable(struct drm_crtc *crtc) +{
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
- struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
- int ret;
- DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
- ret = mtk_smi_larb_get(ovl->larb_dev);
- if (ret) {
DRM_ERROR("Failed to get larb: %d\n", ret);
return;
- }
- ret = mtk_crtc_ddp_hw_init(mtk_crtc);
- if (ret) {
mtk_smi_larb_put(ovl->larb_dev);
return;
- }
- drm_crtc_vblank_on(crtc);
- mtk_crtc->enabled = true;
+}
+static void mtk_drm_crtc_disable(struct drm_crtc *crtc) +{
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
- struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
- int i;
- DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
- if (!mtk_crtc->enabled)
return;
- /* Set all pending plane state to disabled */
- for (i = 0; i < OVL_LAYER_NR; i++) {
struct drm_plane *plane = &mtk_crtc->planes[i].base;
struct mtk_plane_state *plane_state;
plane_state = to_mtk_plane_state(plane->state);
plane_state->pending.enable = false;
plane_state->pending.config = true;
- }
- mtk_crtc->pending_planes = true;
- /* Wait for planes to be disabled */
- drm_crtc_wait_one_vblank(crtc);
- drm_crtc_vblank_off(crtc);
- mtk_crtc_ddp_hw_fini(mtk_crtc);
- mtk_smi_larb_put(ovl->larb_dev);
- mtk_crtc->enabled = false;
+}
+static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
+{
- struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
- if (mtk_crtc->event && state->base.event)
DRM_ERROR("new event while there is still a pending event\n");
- if (state->base.event) {
state->base.event->pipe = drm_crtc_index(crtc);
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
mtk_crtc->event = state->base.event;
state->base.event = NULL;
- }
+}
+static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
+{
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
- unsigned int pending_planes = 0;
- int i;
- if (mtk_crtc->event)
mtk_crtc->pending_needs_vblank = true;
- for (i = 0; i < OVL_LAYER_NR; i++) {
struct drm_plane *plane = &mtk_crtc->planes[i].base;
struct mtk_plane_state *plane_state;
plane_state = to_mtk_plane_state(plane->state);
if (plane_state->pending.dirty) {
plane_state->pending.config = true;
plane_state->pending.dirty = false;
pending_planes |= BIT(i);
}
- }
- if (pending_planes)
mtk_crtc->pending_planes = true;
+}
+static const struct drm_crtc_funcs mtk_crtc_funcs = {
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .destroy = mtk_drm_crtc_destroy,
- .reset = mtk_drm_crtc_reset,
- .atomic_duplicate_state = mtk_drm_crtc_duplicate_state,
- .atomic_destroy_state = mtk_drm_crtc_destroy_state,
+};
+static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
- .mode_fixup = mtk_drm_crtc_mode_fixup,
- .mode_set_nofb = mtk_drm_crtc_mode_set_nofb,
- .enable = mtk_drm_crtc_enable,
- .disable = mtk_drm_crtc_disable,
- .atomic_begin = mtk_drm_crtc_atomic_begin,
- .atomic_flush = mtk_drm_crtc_atomic_flush,
+};
+static int mtk_drm_crtc_init(struct drm_device *drm,
struct mtk_drm_crtc *mtk_crtc,
struct drm_plane *primary,
struct drm_plane *cursor, unsigned int pipe)
+{
- int ret;
- ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
&mtk_crtc_funcs, NULL);
- if (ret)
goto err_cleanup_crtc;
- drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
- return 0;
+err_cleanup_crtc:
- drm_crtc_cleanup(&mtk_crtc->base);
- return ret;
+}
+void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl) +{
- struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
- struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
- unsigned int i;
- /*
* TODO: instead of updating the registers here, we should prepare
* working registers in atomic_commit and let the hardware command
* queue update module registers on vblank.
*/
- if (state->pending_config) {
mtk_ddp_comp_config(ovl, state->pending_width,
state->pending_height,
state->pending_vrefresh);
state->pending_config = false;
- }
- if (mtk_crtc->pending_planes) {
for (i = 0; i < OVL_LAYER_NR; i++) {
struct drm_plane *plane = &mtk_crtc->planes[i].base;
struct mtk_plane_state *plane_state;
plane_state = to_mtk_plane_state(plane->state);
if (plane_state->pending.config) {
mtk_ddp_comp_layer_config(ovl, i, plane_state);
plane_state->pending.config = false;
}
}
mtk_crtc->pending_planes = false;
- }
- mtk_drm_finish_page_flip(mtk_crtc);
+}
+int mtk_drm_crtc_create(struct drm_device *drm_dev,
const enum mtk_ddp_comp_id *path, unsigned int path_len)
+{
- struct mtk_drm_private *priv = drm_dev->dev_private;
- struct device *dev = drm_dev->dev;
- struct mtk_drm_crtc *mtk_crtc;
- enum drm_plane_type type;
- unsigned int zpos;
- int pipe = priv->num_pipes;
- int ret;
- int i;
- for (i = 0; i < path_len; i++) {
enum mtk_ddp_comp_id comp_id = path[i];
struct device_node *node;
node = priv->comp_node[comp_id];
if (!node) {
dev_info(dev,
"Not creating crtc %d because component %d is disabled or missing\n",
pipe, comp_id);
return 0;
}
- }
- mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL);
- if (!mtk_crtc)
return -ENOMEM;
- mtk_crtc->config_regs = priv->config_regs;
- mtk_crtc->ddp_comp_nr = path_len;
- mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
sizeof(*mtk_crtc->ddp_comp),
GFP_KERNEL);
- mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe);
- if (IS_ERR(mtk_crtc->mutex)) {
ret = PTR_ERR(mtk_crtc->mutex);
dev_err(dev, "Failed to get mutex: %d\n", ret);
return ret;
- }
- for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
enum mtk_ddp_comp_id comp_id = path[i];
struct mtk_ddp_comp *comp;
struct device_node *node;
node = priv->comp_node[comp_id];
comp = priv->ddp_comp[comp_id];
if (!comp) {
dev_err(dev, "Component %s not initialized\n",
node->full_name);
ret = -ENODEV;
goto unprepare;
}
ret = clk_prepare(comp->clk);
if (ret) {
dev_err(dev,
"Failed to prepare clock for component %s: %d\n",
node->full_name, ret);
goto unprepare;
}
mtk_crtc->ddp_comp[i] = comp;
- }
- for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) {
type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY :
(zpos == 1) ? DRM_PLANE_TYPE_CURSOR :
DRM_PLANE_TYPE_OVERLAY;
ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos],
BIT(pipe), type, zpos);
if (ret)
goto unprepare;
- }
- ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base,
&mtk_crtc->planes[1].base, pipe);
- if (ret < 0)
goto unprepare;
- priv->crtc[pipe] = &mtk_crtc->base;
- priv->num_pipes++;
- return 0;
+unprepare:
- while (--i >= 0)
clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
- return ret;
+} diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h new file mode 100644 index 0000000..81e5566 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h @@ -0,0 +1,32 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 MTK_DRM_CRTC_H +#define MTK_DRM_CRTC_H
+#include <drm/drm_crtc.h> +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_plane.h"
+#define OVL_LAYER_NR 4
+int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe); +void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe); +void mtk_drm_crtc_check_flush(struct drm_crtc *crtc); +void mtk_drm_crtc_commit(struct drm_crtc *crtc); +void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl); +int mtk_drm_crtc_create(struct drm_device *drm_dev,
const enum mtk_ddp_comp_id *path,
unsigned int path_len);
+#endif /* MTK_DRM_CRTC_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c new file mode 100644 index 0000000..17ba9355 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c @@ -0,0 +1,353 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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/clk.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h>
+#include "mtk_drm_ddp.h" +#include "mtk_drm_ddp_comp.h"
+#define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0x040 +#define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN 0x044 +#define DISP_REG_CONFIG_DISP_OD_MOUT_EN 0x048 +#define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN 0x04c +#define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN 0x050 +#define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084 +#define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088 +#define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac +#define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN 0x0c8 +#define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100
+#define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * (n)) +#define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n)) +#define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * (n)) +#define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * (n))
+#define MUTEX_MOD_DISP_OVL0 BIT(11) +#define MUTEX_MOD_DISP_OVL1 BIT(12) +#define MUTEX_MOD_DISP_RDMA0 BIT(13) +#define MUTEX_MOD_DISP_RDMA1 BIT(14) +#define MUTEX_MOD_DISP_RDMA2 BIT(15) +#define MUTEX_MOD_DISP_WDMA0 BIT(16) +#define MUTEX_MOD_DISP_WDMA1 BIT(17) +#define MUTEX_MOD_DISP_COLOR0 BIT(18) +#define MUTEX_MOD_DISP_COLOR1 BIT(19) +#define MUTEX_MOD_DISP_AAL BIT(20) +#define MUTEX_MOD_DISP_GAMMA BIT(21) +#define MUTEX_MOD_DISP_UFOE BIT(22) +#define MUTEX_MOD_DISP_PWM0 BIT(23) +#define MUTEX_MOD_DISP_PWM1 BIT(24) +#define MUTEX_MOD_DISP_OD BIT(25)
+#define MUTEX_SOF_SINGLE_MODE 0 +#define MUTEX_SOF_DSI0 1 +#define MUTEX_SOF_DSI1 2 +#define MUTEX_SOF_DPI0 3
+#define OVL0_MOUT_EN_COLOR0 0x1 +#define OD_MOUT_EN_RDMA0 0x1 +#define UFOE_MOUT_EN_DSI0 0x1 +#define COLOR0_SEL_IN_OVL0 0x1 +#define OVL1_MOUT_EN_COLOR1 0x1 +#define GAMMA_MOUT_EN_RDMA1 0x1 +#define RDMA1_MOUT_DPI0 0x2 +#define DPI0_SEL_IN_RDMA1 0x1 +#define COLOR1_SEL_IN_OVL1 0x1
+struct mtk_disp_mutex {
- int id;
- bool claimed;
+};
+struct mtk_ddp {
- struct device *dev;
- struct clk *clk;
- void __iomem *regs;
- struct mtk_disp_mutex mutex[10];
+};
+static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
- [DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL,
- [DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0,
- [DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1,
- [DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA,
- [DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD,
- [DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0,
- [DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1,
- [DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0,
- [DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1,
- [DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0,
- [DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1,
- [DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2,
- [DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE,
- [DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0,
- [DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1,
+};
+static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next,
unsigned int *addr)
+{
- unsigned int value;
- if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
*addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
value = OVL0_MOUT_EN_COLOR0;
- } else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
*addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
value = OD_MOUT_EN_RDMA0;
- } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) {
*addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN;
value = UFOE_MOUT_EN_DSI0;
- } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
*addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN;
value = OVL1_MOUT_EN_COLOR1;
- } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) {
*addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN;
value = GAMMA_MOUT_EN_RDMA1;
- } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
*addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN;
value = RDMA1_MOUT_DPI0;
- } else {
value = 0;
- }
- return value;
+}
+static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next,
unsigned int *addr)
+{
- unsigned int value;
- if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
*addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN;
value = COLOR0_SEL_IN_OVL0;
- } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
*addr = DISP_REG_CONFIG_DPI_SEL_IN;
value = DPI0_SEL_IN_RDMA1;
- } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
*addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
value = COLOR1_SEL_IN_OVL1;
- } else {
value = 0;
- }
- return value;
+}
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next)
+{
- unsigned int addr, value, reg;
- value = mtk_ddp_mout_en(cur, next, &addr);
- if (value) {
reg = readl_relaxed(config_regs + addr) | value;
writel_relaxed(reg, config_regs + addr);
- }
- value = mtk_ddp_sel_in(cur, next, &addr);
- if (value) {
reg = readl_relaxed(config_regs + addr) | value;
writel_relaxed(reg, config_regs + addr);
- }
+}
+void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next)
+{
- unsigned int addr, value, reg;
- value = mtk_ddp_mout_en(cur, next, &addr);
- if (value) {
reg = readl_relaxed(config_regs + addr) & ~value;
writel_relaxed(reg, config_regs + addr);
- }
- value = mtk_ddp_sel_in(cur, next, &addr);
- if (value) {
reg = readl_relaxed(config_regs + addr) & ~value;
writel_relaxed(reg, config_regs + addr);
- }
+}
+struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id) +{
- struct mtk_ddp *ddp = dev_get_drvdata(dev);
- if (id >= 10)
return ERR_PTR(-EINVAL);
- if (ddp->mutex[id].claimed)
return ERR_PTR(-EBUSY);
- ddp->mutex[id].claimed = true;
- return &ddp->mutex[id];
+}
+void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex) +{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- WARN_ON(&ddp->mutex[mutex->id] != mutex);
- mutex->claimed = false;
+}
+int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex) +{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- return clk_prepare_enable(ddp->clk);
+}
+void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex) +{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- clk_disable_unprepare(ddp->clk);
+}
+void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
enum mtk_ddp_comp_id id)
+{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- unsigned int reg;
- WARN_ON(&ddp->mutex[mutex->id] != mutex);
- switch (id) {
- case DDP_COMPONENT_DSI0:
reg = MUTEX_SOF_DSI0;
break;
- case DDP_COMPONENT_DSI1:
reg = MUTEX_SOF_DSI0;
break;
- case DDP_COMPONENT_DPI0:
reg = MUTEX_SOF_DPI0;
break;
- default:
reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
reg |= mutex_mod[id];
writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
return;
- }
- writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
+}
+void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
enum mtk_ddp_comp_id id)
+{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- unsigned int reg;
- WARN_ON(&ddp->mutex[mutex->id] != mutex);
- switch (id) {
- case DDP_COMPONENT_DSI0:
- case DDP_COMPONENT_DSI1:
- case DDP_COMPONENT_DPI0:
writel_relaxed(MUTEX_SOF_SINGLE_MODE,
ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
break;
- default:
reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
reg &= ~mutex_mod[id];
writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
break;
- }
+}
+void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex) +{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- WARN_ON(&ddp->mutex[mutex->id] != mutex);
- writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
+}
+void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex) +{
- struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
mutex[mutex->id]);
- WARN_ON(&ddp->mutex[mutex->id] != mutex);
- writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
+}
+static int mtk_ddp_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct mtk_ddp *ddp;
- struct resource *regs;
- int i;
- ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
- if (!ddp)
return -ENOMEM;
- for (i = 0; i < 10; i++)
ddp->mutex[i].id = i;
- ddp->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(ddp->clk)) {
dev_err(dev, "Failed to get clock\n");
return PTR_ERR(ddp->clk);
- }
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ddp->regs = devm_ioremap_resource(dev, regs);
- if (IS_ERR(ddp->regs)) {
dev_err(dev, "Failed to map mutex registers\n");
return PTR_ERR(ddp->regs);
- }
- platform_set_drvdata(pdev, ddp);
- return 0;
+}
+static int mtk_ddp_remove(struct platform_device *pdev) +{
- return 0;
+}
+static const struct of_device_id ddp_driver_dt_match[] = {
- { .compatible = "mediatek,mt8173-disp-mutex" },
- {},
+}; +MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
+struct platform_driver mtk_ddp_driver = {
- .probe = mtk_ddp_probe,
- .remove = mtk_ddp_remove,
- .driver = {
.name = "mediatek-ddp",
.owner = THIS_MODULE,
.of_match_table = ddp_driver_dt_match,
- },
+}; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h new file mode 100644 index 0000000..92c1175 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h @@ -0,0 +1,41 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 MTK_DRM_DDP_H +#define MTK_DRM_DDP_H
+#include "mtk_drm_ddp_comp.h"
+struct regmap; +struct device; +struct mtk_disp_mutex;
+void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next);
+void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
enum mtk_ddp_comp_id cur,
enum mtk_ddp_comp_id next);
+struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id); +int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex); +void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
enum mtk_ddp_comp_id id);
+void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex); +void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex); +void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
enum mtk_ddp_comp_id id);
+void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex); +void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
+#endif /* MTK_DRM_DDP_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c new file mode 100644 index 0000000..3970fcf --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c @@ -0,0 +1,225 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- Authors:
- YT Shen yt.shen@mediatek.com
- CK Hu ck.hu@mediatek.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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/clk.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <drm/drmP.h> +#include "mtk_drm_drv.h" +#include "mtk_drm_plane.h" +#include "mtk_drm_ddp_comp.h"
+#define DISP_OD_EN 0x0000 +#define DISP_OD_INTEN 0x0008 +#define DISP_OD_INTSTA 0x000c +#define DISP_OD_CFG 0x0020 +#define DISP_OD_SIZE 0x0030
+#define DISP_REG_UFO_START 0x0000
+#define DISP_COLOR_CFG_MAIN 0x0400 +#define DISP_COLOR_START 0x0c00 +#define DISP_COLOR_WIDTH 0x0c50 +#define DISP_COLOR_HEIGHT 0x0c54
+#define OD_RELAY_MODE BIT(0)
+#define UFO_BYPASS BIT(2)
+#define COLOR_BYPASS_ALL BIT(7) +#define COLOR_SEQ_SEL BIT(13)
+static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
unsigned int h, unsigned int vrefresh)
+{
- writel(w, comp->regs + DISP_COLOR_WIDTH);
- writel(h, comp->regs + DISP_COLOR_HEIGHT);
+}
+static void mtk_color_start(struct mtk_ddp_comp *comp) +{
- writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
comp->regs + DISP_COLOR_CFG_MAIN);
- writel(0x1, comp->regs + DISP_COLOR_START);
+}
+static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
unsigned int h, unsigned int vrefresh)
+{
- writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
+}
+static void mtk_od_start(struct mtk_ddp_comp *comp) +{
- writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
- writel(1, comp->regs + DISP_OD_EN);
+}
+static void mtk_ufoe_start(struct mtk_ddp_comp *comp) +{
- writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
+}
+static const struct mtk_ddp_comp_funcs ddp_color = {
- .config = mtk_color_config,
- .start = mtk_color_start,
+};
+static const struct mtk_ddp_comp_funcs ddp_od = {
- .config = mtk_od_config,
- .start = mtk_od_start,
+};
+static const struct mtk_ddp_comp_funcs ddp_ufoe = {
- .start = mtk_ufoe_start,
+};
+static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
- [MTK_DISP_OVL] = "ovl",
- [MTK_DISP_RDMA] = "rdma",
- [MTK_DISP_WDMA] = "wdma",
- [MTK_DISP_COLOR] = "color",
- [MTK_DISP_AAL] = "aal",
- [MTK_DISP_GAMMA] = "gamma",
- [MTK_DISP_UFOE] = "ufoe",
- [MTK_DSI] = "dsi",
- [MTK_DPI] = "dpi",
- [MTK_DISP_PWM] = "pwm",
- [MTK_DISP_MUTEX] = "mutex",
- [MTK_DISP_OD] = "od",
+};
+struct mtk_ddp_comp_match {
- enum mtk_ddp_comp_type type;
- int alias_id;
- const struct mtk_ddp_comp_funcs *funcs;
+};
+static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
- [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, NULL },
- [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color },
- [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color },
- [DDP_COMPONENT_DPI0] = { MTK_DPI, 0, NULL },
- [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL },
- [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL },
- [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, NULL },
- [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od },
- [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL },
- [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL },
- [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL },
- [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, NULL },
- [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, NULL },
- [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, NULL },
- [DDP_COMPONENT_UFOE] = { MTK_DISP_UFOE, 0, &ddp_ufoe },
- [DDP_COMPONENT_WDMA0] = { MTK_DISP_WDMA, 0, NULL },
- [DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, NULL },
+};
+int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type)
+{
- int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]);
- int i;
- for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) {
if (comp_type == mtk_ddp_matches[i].type &&
(id < 0 || id == mtk_ddp_matches[i].alias_id))
return i;
- }
- return -EINVAL;
+}
+int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
const struct mtk_ddp_comp_funcs *funcs)
+{
- enum mtk_ddp_comp_type type;
- struct device_node *larb_node;
- struct platform_device *larb_pdev;
- if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
return -EINVAL;
- comp->id = comp_id;
- comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
- if (comp_id == DDP_COMPONENT_DPI0 ||
comp_id == DDP_COMPONENT_DSI0 ||
comp_id == DDP_COMPONENT_PWM0) {
comp->regs = NULL;
comp->clk = NULL;
comp->irq = 0;
return 0;
- }
- comp->regs = of_iomap(node, 0);
- comp->irq = of_irq_get(node, 0);
- comp->clk = of_clk_get(node, 0);
- if (IS_ERR(comp->clk))
comp->clk = NULL;
- type = mtk_ddp_matches[comp_id].type;
- /* Only DMA capable components need the LARB property */
- comp->larb_dev = NULL;
- if (type != MTK_DISP_OVL &&
type != MTK_DISP_RDMA &&
type != MTK_DISP_WDMA)
return 0;
- larb_node = of_parse_phandle(node, "mediatek,larb", 0);
- if (!larb_node) {
dev_err(dev,
"Missing mediadek,larb phandle in %s node\n",
node->full_name);
return -EINVAL;
- }
- larb_pdev = of_find_device_by_node(larb_node);
- if (!larb_pdev) {
dev_warn(dev, "Waiting for larb device %s\n",
larb_node->full_name);
of_node_put(larb_node);
return -EPROBE_DEFER;
- }
- of_node_put(larb_node);
- comp->larb_dev = &larb_pdev->dev;
- return 0;
+}
+int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp) +{
- struct mtk_drm_private *private = drm->dev_private;
- if (private->ddp_comp[comp->id])
return -EBUSY;
- private->ddp_comp[comp->id] = comp;
- return 0;
+}
+void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp) +{
- struct mtk_drm_private *private = drm->dev_private;
- private->ddp_comp[comp->id] = NULL;
+} diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h new file mode 100644 index 0000000..6b13ba9 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h @@ -0,0 +1,150 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 MTK_DRM_DDP_COMP_H +#define MTK_DRM_DDP_COMP_H
+#include <linux/io.h>
+struct device; +struct device_node; +struct drm_crtc; +struct drm_device; +struct mtk_plane_state;
+enum mtk_ddp_comp_type {
- MTK_DISP_OVL,
- MTK_DISP_RDMA,
- MTK_DISP_WDMA,
- MTK_DISP_COLOR,
- MTK_DISP_AAL,
- MTK_DISP_GAMMA,
- MTK_DISP_UFOE,
- MTK_DSI,
- MTK_DPI,
- MTK_DISP_PWM,
- MTK_DISP_MUTEX,
- MTK_DISP_OD,
- MTK_DDP_COMP_TYPE_MAX,
+};
+enum mtk_ddp_comp_id {
- DDP_COMPONENT_AAL,
- DDP_COMPONENT_COLOR0,
- DDP_COMPONENT_COLOR1,
- DDP_COMPONENT_DPI0,
- DDP_COMPONENT_DSI0,
- DDP_COMPONENT_DSI1,
- DDP_COMPONENT_GAMMA,
- DDP_COMPONENT_OD,
- DDP_COMPONENT_OVL0,
- DDP_COMPONENT_OVL1,
- DDP_COMPONENT_PWM0,
- DDP_COMPONENT_PWM1,
- DDP_COMPONENT_RDMA0,
- DDP_COMPONENT_RDMA1,
- DDP_COMPONENT_RDMA2,
- DDP_COMPONENT_UFOE,
- DDP_COMPONENT_WDMA0,
- DDP_COMPONENT_WDMA1,
- DDP_COMPONENT_ID_MAX,
+};
+struct mtk_ddp_comp;
+struct mtk_ddp_comp_funcs {
- void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
unsigned int h, unsigned int vrefresh);
- void (*start)(struct mtk_ddp_comp *comp);
- void (*stop)(struct mtk_ddp_comp *comp);
- void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
- void (*disable_vblank)(struct mtk_ddp_comp *comp);
- void (*layer_on)(struct mtk_ddp_comp *comp, unsigned int idx);
- void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
- void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
struct mtk_plane_state *state);
+};
+struct mtk_ddp_comp {
- struct clk *clk;
- void __iomem *regs;
- int irq;
- struct device *larb_dev;
- enum mtk_ddp_comp_id id;
- const struct mtk_ddp_comp_funcs *funcs;
+};
+static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
unsigned int w, unsigned int h,
unsigned int vrefresh)
+{
- if (comp->funcs && comp->funcs->config)
comp->funcs->config(comp, w, h, vrefresh);
+}
+static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp) +{
- if (comp->funcs && comp->funcs->start)
comp->funcs->start(comp);
+}
+static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp) +{
- if (comp->funcs && comp->funcs->stop)
comp->funcs->stop(comp);
+}
+static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp,
struct drm_crtc *crtc)
+{
- if (comp->funcs && comp->funcs->enable_vblank)
comp->funcs->enable_vblank(comp, crtc);
+}
+static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp) +{
- if (comp->funcs && comp->funcs->disable_vblank)
comp->funcs->disable_vblank(comp);
+}
+static inline void mtk_ddp_comp_layer_on(struct mtk_ddp_comp *comp,
unsigned int idx)
+{
- if (comp->funcs && comp->funcs->layer_on)
comp->funcs->layer_on(comp, idx);
+}
+static inline void mtk_ddp_comp_layer_off(struct mtk_ddp_comp *comp,
unsigned int idx)
+{
- if (comp->funcs && comp->funcs->layer_off)
comp->funcs->layer_off(comp, idx);
+}
+static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
unsigned int idx,
struct mtk_plane_state *state)
+{
- if (comp->funcs && comp->funcs->layer_config)
comp->funcs->layer_config(comp, idx, state);
+}
+int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type);
+int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
const struct mtk_ddp_comp_funcs *funcs);
+int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp); +void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp);
+#endif /* MTK_DRM_DDP_COMP_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c new file mode 100644 index 0000000..ab1b45d --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -0,0 +1,565 @@ +/*
- Copyright (c) 2015 MediaTek Inc.
- Author: YT SHEN yt.shen@mediatek.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License 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 <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_gem.h> +#include <linux/component.h> +#include <linux/iommu.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h>
+#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp.h" +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_drv.h" +#include "mtk_drm_fb.h" +#include "mtk_drm_gem.h"
+#define DRIVER_NAME "mediatek" +#define DRIVER_DESC "Mediatek SoC DRM" +#define DRIVER_DATE "20150513" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0
+static void mtk_atomic_schedule(struct mtk_drm_private *private,
struct drm_atomic_state *state)
+{
- private->commit.state = state;
- schedule_work(&private->commit.work);
+}
+static void mtk_atomic_wait_for_fences(struct drm_atomic_state *state) +{
- struct drm_plane *plane;
- struct drm_plane_state *plane_state;
- int i;
- for_each_plane_in_state(state, plane, plane_state, i)
mtk_fb_wait(plane->state->fb);
+}
+static void mtk_atomic_complete(struct mtk_drm_private *private,
struct drm_atomic_state *state)
+{
- struct drm_device *drm = private->drm;
- mtk_atomic_wait_for_fences(state);
- drm_atomic_helper_commit_modeset_disables(drm, state);
- drm_atomic_helper_commit_planes(drm, state, false);
- drm_atomic_helper_commit_modeset_enables(drm, state);
- drm_atomic_helper_wait_for_vblanks(drm, state);
- drm_atomic_helper_cleanup_planes(drm, state);
- drm_atomic_state_free(state);
+}
+static void mtk_atomic_work(struct work_struct *work) +{
- struct mtk_drm_private *private = container_of(work,
struct mtk_drm_private, commit.work);
- mtk_atomic_complete(private, private->commit.state);
+}
+static int mtk_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *state,
bool async)
+{
- struct mtk_drm_private *private = drm->dev_private;
- int ret;
- ret = drm_atomic_helper_prepare_planes(drm, state);
- if (ret)
return ret;
- mutex_lock(&private->commit.lock);
- flush_work(&private->commit.work);
- drm_atomic_helper_swap_state(drm, state);
- if (async)
mtk_atomic_schedule(private, state);
- else
mtk_atomic_complete(private, state);
- mutex_unlock(&private->commit.lock);
- return 0;
+}
+static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
- .fb_create = mtk_drm_mode_fb_create,
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = mtk_atomic_commit,
+};
+static const enum mtk_ddp_comp_id mtk_ddp_main[] = {
- DDP_COMPONENT_OVL0,
- DDP_COMPONENT_COLOR0,
- DDP_COMPONENT_AAL,
- DDP_COMPONENT_OD,
- DDP_COMPONENT_RDMA0,
- DDP_COMPONENT_UFOE,
- DDP_COMPONENT_DSI0,
- DDP_COMPONENT_PWM0,
+};
+static const enum mtk_ddp_comp_id mtk_ddp_ext[] = {
- DDP_COMPONENT_OVL1,
- DDP_COMPONENT_COLOR1,
- DDP_COMPONENT_GAMMA,
- DDP_COMPONENT_RDMA1,
- DDP_COMPONENT_DPI0,
+};
+static int mtk_drm_kms_init(struct drm_device *drm) +{
- struct mtk_drm_private *private = drm->dev_private;
- struct platform_device *pdev;
- struct device_node *np;
- int ret;
- if (!iommu_present(&platform_bus_type))
return -EPROBE_DEFER;
- pdev = of_find_device_by_node(private->mutex_node);
- if (!pdev) {
dev_err(drm->dev, "Waiting for disp-mutex device %s\n",
private->mutex_node->full_name);
of_node_put(private->mutex_node);
return -EPROBE_DEFER;
- }
- private->mutex_dev = &pdev->dev;
- drm_mode_config_init(drm);
- drm->mode_config.min_width = 64;
- drm->mode_config.min_height = 64;
- /*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
- drm->mode_config.max_width = 4096;
- drm->mode_config.max_height = 4096;
- drm->mode_config.funcs = &mtk_drm_mode_config_funcs;
- ret = component_bind_all(drm->dev, drm);
- if (ret)
goto err_config_cleanup;
- /*
* We currently support two fixed data streams, each optional,
* and each statically assigned to a crtc:
* OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ...
*/
- ret = mtk_drm_crtc_create(drm, mtk_ddp_main, ARRAY_SIZE(mtk_ddp_main));
- if (ret < 0)
goto err_component_unbind;
- /* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */
- ret = mtk_drm_crtc_create(drm, mtk_ddp_ext, ARRAY_SIZE(mtk_ddp_ext));
- if (ret < 0)
goto err_component_unbind;
- /* Use OVL device for all DMA memory allocations */
- np = private->comp_node[mtk_ddp_main[0]] ?:
private->comp_node[mtk_ddp_ext[0]];
- pdev = of_find_device_by_node(np);
- if (!pdev) {
ret = -ENODEV;
dev_err(drm->dev, "Need at least one OVL device\n");
goto err_component_unbind;
- }
- private->dma_dev = &pdev->dev;
- /*
* We don't use the drm_irq_install() helpers provided by the DRM
* core, so we need to set this manually in order to allow the
* DRM_IOCTL_WAIT_VBLANK to operate correctly.
*/
- drm->irq_enabled = true;
- ret = drm_vblank_init(drm, MAX_CRTC);
- if (ret < 0)
goto err_component_unbind;
- drm_kms_helper_poll_init(drm);
- drm_mode_config_reset(drm);
- return 0;
+err_component_unbind:
- component_unbind_all(drm->dev, drm);
+err_config_cleanup:
- drm_mode_config_cleanup(drm);
- return ret;
+}
+static void mtk_drm_kms_deinit(struct drm_device *drm) +{
- drm_kms_helper_poll_fini(drm);
- drm_vblank_cleanup(drm);
- component_unbind_all(drm->dev, drm);
- drm_mode_config_cleanup(drm);
+}
+static int mtk_drm_unload(struct drm_device *drm) +{
- mtk_drm_kms_deinit(drm);
- drm->dev_private = NULL;
- return 0;
+}
Like load, the unload hook is deprecated. And in drm-next we now have some nice helpers to make not using an unload hook easier. Please refactor per existing examples and get rid of your unload hook.
+static const struct vm_operations_struct mtk_drm_gem_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+};
Drive-by review I just spotted: Please use drm_gem_cma_vm_ops instead of rolling your own.
Thanks, Daniel