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@xxxxxxxxxxxx> > 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@xxxxxxxxxxxx> > Signed-off-by: YT Shen <yt.shen@xxxxxxxxxxxx> > Signed-off-by: Daniel Kurtz <djkurtz@xxxxxxxxxxxx> > Signed-off-by: Bibby Hsieh <bibby.hsieh@xxxxxxxxxxxx> > Signed-off-by: Mao Huang <littlecvr@xxxxxxxxxxxx> > Signed-off-by: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx> > --- > 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@xxxxxxxxxxxx> > + * CK Hu <ck.hu@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxxxxx> > + * > + * 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 -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html