mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									923dd88d3c
								
							
						
					
					
						commit
						119f517362
					
				
					 20 changed files with 3389 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -288,3 +288,5 @@ source "drivers/gpu/drm/etnaviv/Kconfig"
 | 
			
		|||
source "drivers/gpu/drm/arc/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/gpu/drm/hisilicon/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/gpu/drm/mediatek/Kconfig"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,6 +74,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/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								drivers/gpu/drm/mediatek/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								drivers/gpu/drm/mediatek/Kconfig
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
config DRM_MEDIATEK
 | 
			
		||||
	tristate "DRM Support for Mediatek SoCs"
 | 
			
		||||
	depends on DRM
 | 
			
		||||
	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
 | 
			
		||||
	select DRM_GEM_CMA_HELPER
 | 
			
		||||
	select DRM_KMS_HELPER
 | 
			
		||||
	select IOMMU_DMA
 | 
			
		||||
	select MEMORY
 | 
			
		||||
	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.
 | 
			
		||||
							
								
								
									
										11
									
								
								drivers/gpu/drm/mediatek/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								drivers/gpu/drm/mediatek/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
							
								
								
									
										302
									
								
								drivers/gpu/drm/mediatek/mtk_disp_ovl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								drivers/gpu/drm/mediatek/mtk_disp_ovl.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										240
									
								
								drivers/gpu/drm/mediatek/mtk_disp_rdma.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								drivers/gpu/drm/mediatek/mtk_disp_rdma.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										582
									
								
								drivers/gpu/drm/mediatek/mtk_drm_crtc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										582
									
								
								drivers/gpu/drm/mediatek/mtk_drm_crtc.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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_crtc_send_vblank_event(crtc, 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								drivers/gpu/drm/mediatek/mtk_drm_crtc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								drivers/gpu/drm/mediatek/mtk_drm_crtc.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
							
								
								
									
										353
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										41
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
							
								
								
									
										225
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
							
								
								
									
										564
									
								
								drivers/gpu/drm/mediatek/mtk_drm_drv.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										564
									
								
								drivers/gpu/drm/mediatek/mtk_drm_drv.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,564 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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 <drm/drm_gem_cma_helper.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 const struct file_operations mtk_drm_fops = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.open = drm_open,
 | 
			
		||||
	.release = drm_release,
 | 
			
		||||
	.unlocked_ioctl = drm_ioctl,
 | 
			
		||||
	.mmap = mtk_drm_gem_mmap,
 | 
			
		||||
	.poll = drm_poll,
 | 
			
		||||
	.read = drm_read,
 | 
			
		||||
#ifdef CONFIG_COMPAT
 | 
			
		||||
	.compat_ioctl = drm_compat_ioctl,
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct drm_driver mtk_drm_driver = {
 | 
			
		||||
	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
 | 
			
		||||
			   DRIVER_ATOMIC,
 | 
			
		||||
 | 
			
		||||
	.get_vblank_counter = drm_vblank_count,
 | 
			
		||||
	.enable_vblank = mtk_drm_crtc_enable_vblank,
 | 
			
		||||
	.disable_vblank = mtk_drm_crtc_disable_vblank,
 | 
			
		||||
 | 
			
		||||
	.gem_free_object = mtk_drm_gem_free_object,
 | 
			
		||||
	.gem_vm_ops = &drm_gem_cma_vm_ops,
 | 
			
		||||
	.dumb_create = mtk_drm_gem_dumb_create,
 | 
			
		||||
	.dumb_map_offset = mtk_drm_gem_dumb_map_offset,
 | 
			
		||||
	.dumb_destroy = drm_gem_dumb_destroy,
 | 
			
		||||
 | 
			
		||||
	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
 | 
			
		||||
	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 | 
			
		||||
	.gem_prime_export = drm_gem_prime_export,
 | 
			
		||||
	.gem_prime_import = drm_gem_prime_import,
 | 
			
		||||
	.gem_prime_get_sg_table = mtk_gem_prime_get_sg_table,
 | 
			
		||||
	.gem_prime_import_sg_table = mtk_gem_prime_import_sg_table,
 | 
			
		||||
	.gem_prime_mmap = mtk_drm_gem_mmap_buf,
 | 
			
		||||
	.fops = &mtk_drm_fops,
 | 
			
		||||
 | 
			
		||||
	.name = DRIVER_NAME,
 | 
			
		||||
	.desc = DRIVER_DESC,
 | 
			
		||||
	.date = DRIVER_DATE,
 | 
			
		||||
	.major = DRIVER_MAJOR,
 | 
			
		||||
	.minor = DRIVER_MINOR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int compare_of(struct device *dev, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return dev->of_node == data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_drm_bind(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_private *private = dev_get_drvdata(dev);
 | 
			
		||||
	struct drm_device *drm;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	drm = drm_dev_alloc(&mtk_drm_driver, dev);
 | 
			
		||||
	if (!drm)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	drm_dev_set_unique(drm, dev_name(dev));
 | 
			
		||||
 | 
			
		||||
	drm->dev_private = private;
 | 
			
		||||
	private->drm = drm;
 | 
			
		||||
 | 
			
		||||
	ret = mtk_drm_kms_init(drm);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		goto err_free;
 | 
			
		||||
 | 
			
		||||
	ret = drm_dev_register(drm, 0);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		goto err_deinit;
 | 
			
		||||
 | 
			
		||||
	ret = drm_connector_register_all(drm);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		goto err_unregister;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_unregister:
 | 
			
		||||
	drm_dev_unregister(drm);
 | 
			
		||||
err_deinit:
 | 
			
		||||
	mtk_drm_kms_deinit(drm);
 | 
			
		||||
err_free:
 | 
			
		||||
	drm_dev_unref(drm);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mtk_drm_unbind(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_private *private = dev_get_drvdata(dev);
 | 
			
		||||
 | 
			
		||||
	drm_put_dev(private->drm);
 | 
			
		||||
	private->drm = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct component_master_ops mtk_drm_ops = {
 | 
			
		||||
	.bind		= mtk_drm_bind,
 | 
			
		||||
	.unbind		= mtk_drm_unbind,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-ovl",   .data = (void *)MTK_DISP_OVL },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-aal",   .data = (void *)MTK_DISP_AAL},
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-ufoe",  .data = (void *)MTK_DISP_UFOE },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-dsi",        .data = (void *)MTK_DSI },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-dpi",        .data = (void *)MTK_DPI },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-pwm",   .data = (void *)MTK_DISP_PWM },
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-disp-od",    .data = (void *)MTK_DISP_OD },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtk_drm_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = &pdev->dev;
 | 
			
		||||
	struct mtk_drm_private *private;
 | 
			
		||||
	struct resource *mem;
 | 
			
		||||
	struct device_node *node;
 | 
			
		||||
	struct component_match *match = NULL;
 | 
			
		||||
	int ret;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL);
 | 
			
		||||
	if (!private)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	mutex_init(&private->commit.lock);
 | 
			
		||||
	INIT_WORK(&private->commit.work, mtk_atomic_work);
 | 
			
		||||
 | 
			
		||||
	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
			
		||||
	private->config_regs = devm_ioremap_resource(dev, mem);
 | 
			
		||||
	if (IS_ERR(private->config_regs)) {
 | 
			
		||||
		ret = PTR_ERR(private->config_regs);
 | 
			
		||||
		dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n",
 | 
			
		||||
			ret);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Iterate over sibling DISP function blocks */
 | 
			
		||||
	for_each_child_of_node(dev->of_node->parent, node) {
 | 
			
		||||
		const struct of_device_id *of_id;
 | 
			
		||||
		enum mtk_ddp_comp_type comp_type;
 | 
			
		||||
		int comp_id;
 | 
			
		||||
 | 
			
		||||
		of_id = of_match_node(mtk_ddp_comp_dt_ids, node);
 | 
			
		||||
		if (!of_id)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!of_device_is_available(node)) {
 | 
			
		||||
			dev_dbg(dev, "Skipping disabled component %s\n",
 | 
			
		||||
				node->full_name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		comp_type = (enum mtk_ddp_comp_type)of_id->data;
 | 
			
		||||
 | 
			
		||||
		if (comp_type == MTK_DISP_MUTEX) {
 | 
			
		||||
			private->mutex_node = of_node_get(node);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		comp_id = mtk_ddp_comp_get_id(node, comp_type);
 | 
			
		||||
		if (comp_id < 0) {
 | 
			
		||||
			dev_warn(dev, "Skipping unknown component %s\n",
 | 
			
		||||
				 node->full_name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private->comp_node[comp_id] = of_node_get(node);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Currently only the OVL, RDMA, DSI, and DPI blocks have
 | 
			
		||||
		 * separate component platform drivers and initialize their own
 | 
			
		||||
		 * DDP component structure. The others are initialized here.
 | 
			
		||||
		 */
 | 
			
		||||
		if (comp_type == MTK_DISP_OVL ||
 | 
			
		||||
		    comp_type == MTK_DISP_RDMA ||
 | 
			
		||||
		    comp_type == MTK_DSI ||
 | 
			
		||||
		    comp_type == MTK_DPI) {
 | 
			
		||||
			dev_info(dev, "Adding component match for %s\n",
 | 
			
		||||
				 node->full_name);
 | 
			
		||||
			component_match_add(dev, &match, compare_of, node);
 | 
			
		||||
		} else {
 | 
			
		||||
			struct mtk_ddp_comp *comp;
 | 
			
		||||
 | 
			
		||||
			comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
 | 
			
		||||
			if (!comp) {
 | 
			
		||||
				ret = -ENOMEM;
 | 
			
		||||
				goto err_node;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ret = mtk_ddp_comp_init(dev, node, comp, comp_id, NULL);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				goto err_node;
 | 
			
		||||
 | 
			
		||||
			private->ddp_comp[comp_id] = comp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!private->mutex_node) {
 | 
			
		||||
		dev_err(dev, "Failed to find disp-mutex node\n");
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_node;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pm_runtime_enable(dev);
 | 
			
		||||
 | 
			
		||||
	platform_set_drvdata(pdev, private);
 | 
			
		||||
 | 
			
		||||
	ret = component_master_add_with_match(dev, &mtk_drm_ops, match);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err_pm;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_pm:
 | 
			
		||||
	pm_runtime_disable(dev);
 | 
			
		||||
err_node:
 | 
			
		||||
	of_node_put(private->mutex_node);
 | 
			
		||||
	for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
 | 
			
		||||
		of_node_put(private->comp_node[i]);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_drm_remove(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_private *private = platform_get_drvdata(pdev);
 | 
			
		||||
	struct drm_device *drm = private->drm;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	drm_connector_unregister_all(drm);
 | 
			
		||||
	drm_dev_unregister(drm);
 | 
			
		||||
	mtk_drm_kms_deinit(drm);
 | 
			
		||||
	drm_dev_unref(drm);
 | 
			
		||||
 | 
			
		||||
	component_master_del(&pdev->dev, &mtk_drm_ops);
 | 
			
		||||
	pm_runtime_disable(&pdev->dev);
 | 
			
		||||
	of_node_put(private->mutex_node);
 | 
			
		||||
	for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
 | 
			
		||||
		of_node_put(private->comp_node[i]);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_PM_SLEEP
 | 
			
		||||
static int mtk_drm_sys_suspend(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_private *private = dev_get_drvdata(dev);
 | 
			
		||||
	struct drm_device *drm = private->drm;
 | 
			
		||||
 | 
			
		||||
	drm_kms_helper_poll_disable(drm);
 | 
			
		||||
 | 
			
		||||
	private->suspend_state = drm_atomic_helper_suspend(drm);
 | 
			
		||||
	if (IS_ERR(private->suspend_state)) {
 | 
			
		||||
		drm_kms_helper_poll_enable(drm);
 | 
			
		||||
		return PTR_ERR(private->suspend_state);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DRM_DEBUG_DRIVER("mtk_drm_sys_suspend\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_drm_sys_resume(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_private *private = dev_get_drvdata(dev);
 | 
			
		||||
	struct drm_device *drm = private->drm;
 | 
			
		||||
 | 
			
		||||
	drm_atomic_helper_resume(drm, private->suspend_state);
 | 
			
		||||
	drm_kms_helper_poll_enable(drm);
 | 
			
		||||
 | 
			
		||||
	DRM_DEBUG_DRIVER("mtk_drm_sys_resume\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
 | 
			
		||||
			 mtk_drm_sys_resume);
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtk_drm_of_ids[] = {
 | 
			
		||||
	{ .compatible = "mediatek,mt8173-mmsys", },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct platform_driver mtk_drm_platform_driver = {
 | 
			
		||||
	.probe	= mtk_drm_probe,
 | 
			
		||||
	.remove	= mtk_drm_remove,
 | 
			
		||||
	.driver	= {
 | 
			
		||||
		.name	= "mediatek-drm",
 | 
			
		||||
		.of_match_table = mtk_drm_of_ids,
 | 
			
		||||
		.pm     = &mtk_drm_pm_ops,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct platform_driver * const mtk_drm_drivers[] = {
 | 
			
		||||
	&mtk_ddp_driver,
 | 
			
		||||
	&mtk_disp_ovl_driver,
 | 
			
		||||
	&mtk_disp_rdma_driver,
 | 
			
		||||
	&mtk_drm_platform_driver,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtk_drm_init(void)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) {
 | 
			
		||||
		ret = platform_driver_register(mtk_drm_drivers[i]);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			pr_err("Failed to register %s driver: %d\n",
 | 
			
		||||
			       mtk_drm_drivers[i]->driver.name, ret);
 | 
			
		||||
			goto err;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	while (--i >= 0)
 | 
			
		||||
		platform_driver_unregister(mtk_drm_drivers[i]);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __exit mtk_drm_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--)
 | 
			
		||||
		platform_driver_unregister(mtk_drm_drivers[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtk_drm_init);
 | 
			
		||||
module_exit(mtk_drm_exit);
 | 
			
		||||
 | 
			
		||||
MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>");
 | 
			
		||||
MODULE_DESCRIPTION("Mediatek SoC DRM driver");
 | 
			
		||||
MODULE_LICENSE("GPL v2");
 | 
			
		||||
							
								
								
									
										57
									
								
								drivers/gpu/drm/mediatek/mtk_drm_drv.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								drivers/gpu/drm/mediatek/mtk_drm_drv.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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_DRV_H
 | 
			
		||||
#define MTK_DRM_DRV_H
 | 
			
		||||
 | 
			
		||||
#include <linux/io.h>
 | 
			
		||||
#include "mtk_drm_ddp_comp.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_CRTC	2
 | 
			
		||||
#define MAX_CONNECTOR	2
 | 
			
		||||
 | 
			
		||||
struct device;
 | 
			
		||||
struct device_node;
 | 
			
		||||
struct drm_crtc;
 | 
			
		||||
struct drm_device;
 | 
			
		||||
struct drm_fb_helper;
 | 
			
		||||
struct drm_property;
 | 
			
		||||
struct regmap;
 | 
			
		||||
 | 
			
		||||
struct mtk_drm_private {
 | 
			
		||||
	struct drm_device *drm;
 | 
			
		||||
	struct device *dma_dev;
 | 
			
		||||
 | 
			
		||||
	struct drm_crtc *crtc[MAX_CRTC];
 | 
			
		||||
	unsigned int num_pipes;
 | 
			
		||||
 | 
			
		||||
	struct device_node *mutex_node;
 | 
			
		||||
	struct device *mutex_dev;
 | 
			
		||||
	void __iomem *config_regs;
 | 
			
		||||
	struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
 | 
			
		||||
	struct mtk_ddp_comp *ddp_comp[DDP_COMPONENT_ID_MAX];
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		struct drm_atomic_state *state;
 | 
			
		||||
		struct work_struct work;
 | 
			
		||||
		struct mutex lock;
 | 
			
		||||
	} commit;
 | 
			
		||||
 | 
			
		||||
	struct drm_atomic_state *suspend_state;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct platform_driver mtk_ddp_driver;
 | 
			
		||||
extern struct platform_driver mtk_disp_ovl_driver;
 | 
			
		||||
extern struct platform_driver mtk_disp_rdma_driver;
 | 
			
		||||
 | 
			
		||||
#endif /* MTK_DRM_DRV_H */
 | 
			
		||||
							
								
								
									
										165
									
								
								drivers/gpu/drm/mediatek/mtk_drm_fb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								drivers/gpu/drm/mediatek/mtk_drm_fb.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,165 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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 <drm/drm_crtc_helper.h>
 | 
			
		||||
#include <drm/drm_fb_helper.h>
 | 
			
		||||
#include <drm/drm_gem.h>
 | 
			
		||||
#include <linux/dma-buf.h>
 | 
			
		||||
#include <linux/reservation.h>
 | 
			
		||||
 | 
			
		||||
#include "mtk_drm_drv.h"
 | 
			
		||||
#include "mtk_drm_fb.h"
 | 
			
		||||
#include "mtk_drm_gem.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * mtk specific framebuffer structure.
 | 
			
		||||
 *
 | 
			
		||||
 * @fb: drm framebuffer object.
 | 
			
		||||
 * @gem_obj: array of gem objects.
 | 
			
		||||
 */
 | 
			
		||||
struct mtk_drm_fb {
 | 
			
		||||
	struct drm_framebuffer	base;
 | 
			
		||||
	/* For now we only support a single plane */
 | 
			
		||||
	struct drm_gem_object	*gem_obj;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base)
 | 
			
		||||
 | 
			
		||||
struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
 | 
			
		||||
 | 
			
		||||
	return mtk_fb->gem_obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb,
 | 
			
		||||
				    struct drm_file *file_priv,
 | 
			
		||||
				    unsigned int *handle)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
 | 
			
		||||
 | 
			
		||||
	return drm_gem_handle_create(file_priv, mtk_fb->gem_obj, handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
 | 
			
		||||
 | 
			
		||||
	drm_framebuffer_cleanup(fb);
 | 
			
		||||
 | 
			
		||||
	drm_gem_object_unreference_unlocked(mtk_fb->gem_obj);
 | 
			
		||||
 | 
			
		||||
	kfree(mtk_fb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = {
 | 
			
		||||
	.create_handle = mtk_drm_fb_create_handle,
 | 
			
		||||
	.destroy = mtk_drm_fb_destroy,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev,
 | 
			
		||||
					const struct drm_mode_fb_cmd2 *mode,
 | 
			
		||||
					struct drm_gem_object *obj)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_fb *mtk_fb;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (drm_format_num_planes(mode->pixel_format) != 1)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL);
 | 
			
		||||
	if (!mtk_fb)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode);
 | 
			
		||||
 | 
			
		||||
	mtk_fb->gem_obj = obj;
 | 
			
		||||
 | 
			
		||||
	ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		DRM_ERROR("failed to initialize framebuffer\n");
 | 
			
		||||
		kfree(mtk_fb);
 | 
			
		||||
		return ERR_PTR(ret);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mtk_fb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Wait for any exclusive fence in fb's gem object's reservation object.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns -ERESTARTSYS if interrupted, else 0.
 | 
			
		||||
 */
 | 
			
		||||
int mtk_fb_wait(struct drm_framebuffer *fb)
 | 
			
		||||
{
 | 
			
		||||
	struct drm_gem_object *gem;
 | 
			
		||||
	struct reservation_object *resv;
 | 
			
		||||
	long ret;
 | 
			
		||||
 | 
			
		||||
	if (!fb)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	gem = mtk_fb_get_gem_obj(fb);
 | 
			
		||||
	if (!gem || !gem->dma_buf || !gem->dma_buf->resv)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	resv = gem->dma_buf->resv;
 | 
			
		||||
	ret = reservation_object_wait_timeout_rcu(resv, false, true,
 | 
			
		||||
						  MAX_SCHEDULE_TIMEOUT);
 | 
			
		||||
	/* MAX_SCHEDULE_TIMEOUT on success, -ERESTARTSYS if interrupted */
 | 
			
		||||
	if (WARN_ON(ret < 0))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
 | 
			
		||||
					       struct drm_file *file,
 | 
			
		||||
					       const struct drm_mode_fb_cmd2 *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_fb *mtk_fb;
 | 
			
		||||
	struct drm_gem_object *gem;
 | 
			
		||||
	unsigned int width = cmd->width;
 | 
			
		||||
	unsigned int height = cmd->height;
 | 
			
		||||
	unsigned int size, bpp;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (drm_format_num_planes(cmd->pixel_format) != 1)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	gem = drm_gem_object_lookup(dev, file, cmd->handles[0]);
 | 
			
		||||
	if (!gem)
 | 
			
		||||
		return ERR_PTR(-ENOENT);
 | 
			
		||||
 | 
			
		||||
	bpp = drm_format_plane_cpp(cmd->pixel_format, 0);
 | 
			
		||||
	size = (height - 1) * cmd->pitches[0] + width * bpp;
 | 
			
		||||
	size += cmd->offsets[0];
 | 
			
		||||
 | 
			
		||||
	if (gem->size < size) {
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto unreference;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
 | 
			
		||||
	if (IS_ERR(mtk_fb)) {
 | 
			
		||||
		ret = PTR_ERR(mtk_fb);
 | 
			
		||||
		goto unreference;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &mtk_fb->base;
 | 
			
		||||
 | 
			
		||||
unreference:
 | 
			
		||||
	drm_gem_object_unreference_unlocked(gem);
 | 
			
		||||
	return ERR_PTR(ret);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								drivers/gpu/drm/mediatek/mtk_drm_fb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								drivers/gpu/drm/mediatek/mtk_drm_fb.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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_FB_H
 | 
			
		||||
#define MTK_DRM_FB_H
 | 
			
		||||
 | 
			
		||||
struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb);
 | 
			
		||||
int mtk_fb_wait(struct drm_framebuffer *fb);
 | 
			
		||||
struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
 | 
			
		||||
					       struct drm_file *file,
 | 
			
		||||
					       const struct drm_mode_fb_cmd2 *cmd);
 | 
			
		||||
 | 
			
		||||
#endif /* MTK_DRM_FB_H */
 | 
			
		||||
							
								
								
									
										269
									
								
								drivers/gpu/drm/mediatek/mtk_drm_gem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								drivers/gpu/drm/mediatek/mtk_drm_gem.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,269 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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 <drm/drm_gem.h>
 | 
			
		||||
#include <linux/dma-buf.h>
 | 
			
		||||
 | 
			
		||||
#include "mtk_drm_drv.h"
 | 
			
		||||
#include "mtk_drm_gem.h"
 | 
			
		||||
 | 
			
		||||
static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
 | 
			
		||||
						unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem_obj;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	size = round_up(size, PAGE_SIZE);
 | 
			
		||||
 | 
			
		||||
	mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL);
 | 
			
		||||
	if (!mtk_gem_obj)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		DRM_ERROR("failed to initialize gem object\n");
 | 
			
		||||
		kfree(mtk_gem_obj);
 | 
			
		||||
		return ERR_PTR(ret);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mtk_gem_obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
 | 
			
		||||
					   size_t size, bool alloc_kmap)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_private *priv = dev->dev_private;
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem;
 | 
			
		||||
	struct drm_gem_object *obj;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mtk_gem = mtk_drm_gem_init(dev, size);
 | 
			
		||||
	if (IS_ERR(mtk_gem))
 | 
			
		||||
		return ERR_CAST(mtk_gem);
 | 
			
		||||
 | 
			
		||||
	obj = &mtk_gem->base;
 | 
			
		||||
 | 
			
		||||
	init_dma_attrs(&mtk_gem->dma_attrs);
 | 
			
		||||
	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
 | 
			
		||||
 | 
			
		||||
	if (!alloc_kmap)
 | 
			
		||||
		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
 | 
			
		||||
 | 
			
		||||
	mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
 | 
			
		||||
					  &mtk_gem->dma_addr, GFP_KERNEL,
 | 
			
		||||
					  &mtk_gem->dma_attrs);
 | 
			
		||||
	if (!mtk_gem->cookie) {
 | 
			
		||||
		DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
 | 
			
		||||
		ret = -ENOMEM;
 | 
			
		||||
		goto err_gem_free;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (alloc_kmap)
 | 
			
		||||
		mtk_gem->kvaddr = mtk_gem->cookie;
 | 
			
		||||
 | 
			
		||||
	DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n",
 | 
			
		||||
			 mtk_gem->cookie, &mtk_gem->dma_addr,
 | 
			
		||||
			 size);
 | 
			
		||||
 | 
			
		||||
	return mtk_gem;
 | 
			
		||||
 | 
			
		||||
err_gem_free:
 | 
			
		||||
	drm_gem_object_release(obj);
 | 
			
		||||
	kfree(mtk_gem);
 | 
			
		||||
	return ERR_PTR(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mtk_drm_gem_free_object(struct drm_gem_object *obj)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
 | 
			
		||||
	struct mtk_drm_private *priv = obj->dev->dev_private;
 | 
			
		||||
 | 
			
		||||
	if (mtk_gem->sg)
 | 
			
		||||
		drm_prime_gem_destroy(obj, mtk_gem->sg);
 | 
			
		||||
	else
 | 
			
		||||
		dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
 | 
			
		||||
			       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
 | 
			
		||||
 | 
			
		||||
	/* release file pointer to gem object. */
 | 
			
		||||
	drm_gem_object_release(obj);
 | 
			
		||||
 | 
			
		||||
	kfree(mtk_gem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
 | 
			
		||||
			    struct drm_mode_create_dumb *args)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
 | 
			
		||||
	args->size = args->pitch * args->height;
 | 
			
		||||
 | 
			
		||||
	mtk_gem = mtk_drm_gem_create(dev, args->size, false);
 | 
			
		||||
	if (IS_ERR(mtk_gem))
 | 
			
		||||
		return PTR_ERR(mtk_gem);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * allocate a id of idr table where the obj is registered
 | 
			
		||||
	 * and handle has the id what user can see.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err_handle_create;
 | 
			
		||||
 | 
			
		||||
	/* drop reference from allocate - handle holds it now. */
 | 
			
		||||
	drm_gem_object_unreference_unlocked(&mtk_gem->base);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_handle_create:
 | 
			
		||||
	mtk_drm_gem_free_object(&mtk_gem->base);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
 | 
			
		||||
				struct drm_device *dev, uint32_t handle,
 | 
			
		||||
				uint64_t *offset)
 | 
			
		||||
{
 | 
			
		||||
	struct drm_gem_object *obj;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	obj = drm_gem_object_lookup(dev, file_priv, handle);
 | 
			
		||||
	if (!obj) {
 | 
			
		||||
		DRM_ERROR("failed to lookup gem object.\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = drm_gem_create_mmap_offset(obj);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	*offset = drm_vma_node_offset_addr(&obj->vma_node);
 | 
			
		||||
	DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	drm_gem_object_unreference_unlocked(obj);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
 | 
			
		||||
				   struct vm_area_struct *vma)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
 | 
			
		||||
	struct mtk_drm_private *priv = obj->dev->dev_private;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear
 | 
			
		||||
	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
 | 
			
		||||
	 */
 | 
			
		||||
	vma->vm_flags &= ~VM_PFNMAP;
 | 
			
		||||
	vma->vm_pgoff = 0;
 | 
			
		||||
 | 
			
		||||
	ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
 | 
			
		||||
			     mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		drm_gem_vm_close(vma);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = drm_gem_mmap_obj(obj, obj->size, vma);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return mtk_drm_gem_object_mmap(obj, vma);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
 | 
			
		||||
{
 | 
			
		||||
	struct drm_gem_object *obj;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = drm_gem_mmap(filp, vma);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	obj = vma->vm_private_data;
 | 
			
		||||
 | 
			
		||||
	return mtk_drm_gem_object_mmap(obj, vma);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Allocate a sg_table for this GEM object.
 | 
			
		||||
 * Note: Both the table's contents, and the sg_table itself must be freed by
 | 
			
		||||
 *       the caller.
 | 
			
		||||
 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
 | 
			
		||||
 */
 | 
			
		||||
struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
 | 
			
		||||
	struct mtk_drm_private *priv = obj->dev->dev_private;
 | 
			
		||||
	struct sg_table *sgt;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
 | 
			
		||||
	if (!sgt)
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 | 
			
		||||
	ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
 | 
			
		||||
				    mtk_gem->dma_addr, obj->size,
 | 
			
		||||
				    &mtk_gem->dma_attrs);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		DRM_ERROR("failed to allocate sgt, %d\n", ret);
 | 
			
		||||
		kfree(sgt);
 | 
			
		||||
		return ERR_PTR(ret);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sgt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
 | 
			
		||||
			struct dma_buf_attachment *attach, struct sg_table *sg)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem;
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct scatterlist *s;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	dma_addr_t expected;
 | 
			
		||||
 | 
			
		||||
	mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size);
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(mtk_gem))
 | 
			
		||||
		return ERR_PTR(PTR_ERR(mtk_gem));
 | 
			
		||||
 | 
			
		||||
	expected = sg_dma_address(sg->sgl);
 | 
			
		||||
	for_each_sg(sg->sgl, s, sg->nents, i) {
 | 
			
		||||
		if (sg_dma_address(s) != expected) {
 | 
			
		||||
			DRM_ERROR("sg_table is not contiguous");
 | 
			
		||||
			ret = -EINVAL;
 | 
			
		||||
			goto err_gem_free;
 | 
			
		||||
		}
 | 
			
		||||
		expected = sg_dma_address(s) + sg_dma_len(s);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mtk_gem->dma_addr = sg_dma_address(sg->sgl);
 | 
			
		||||
	mtk_gem->sg = sg;
 | 
			
		||||
 | 
			
		||||
	return &mtk_gem->base;
 | 
			
		||||
 | 
			
		||||
err_gem_free:
 | 
			
		||||
	kfree(mtk_gem);
 | 
			
		||||
	return ERR_PTR(ret);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								drivers/gpu/drm/mediatek/mtk_drm_gem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								drivers/gpu/drm/mediatek/mtk_drm_gem.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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_GEM_H_
 | 
			
		||||
#define _MTK_DRM_GEM_H_
 | 
			
		||||
 | 
			
		||||
#include <drm/drm_gem.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * mtk drm buffer structure.
 | 
			
		||||
 *
 | 
			
		||||
 * @base: a gem object.
 | 
			
		||||
 *	- a new handle to this gem object would be created
 | 
			
		||||
 *	by drm_gem_handle_create().
 | 
			
		||||
 * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs()
 | 
			
		||||
 * @kvaddr: kernel virtual address of gem buffer.
 | 
			
		||||
 * @dma_addr: dma address of gem buffer.
 | 
			
		||||
 * @dma_attrs: dma attributes of gem buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * P.S. this object would be transferred to user as kms_bo.handle so
 | 
			
		||||
 *	user can access the buffer through kms_bo.handle.
 | 
			
		||||
 */
 | 
			
		||||
struct mtk_drm_gem_obj {
 | 
			
		||||
	struct drm_gem_object	base;
 | 
			
		||||
	void			*cookie;
 | 
			
		||||
	void			*kvaddr;
 | 
			
		||||
	dma_addr_t		dma_addr;
 | 
			
		||||
	struct dma_attrs	dma_attrs;
 | 
			
		||||
	struct sg_table		*sg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define to_mtk_gem_obj(x)	container_of(x, struct mtk_drm_gem_obj, base)
 | 
			
		||||
 | 
			
		||||
void mtk_drm_gem_free_object(struct drm_gem_object *gem);
 | 
			
		||||
struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, size_t size,
 | 
			
		||||
					   bool alloc_kmap);
 | 
			
		||||
int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
 | 
			
		||||
			    struct drm_mode_create_dumb *args);
 | 
			
		||||
int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
 | 
			
		||||
				struct drm_device *dev, uint32_t handle,
 | 
			
		||||
				uint64_t *offset);
 | 
			
		||||
int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 | 
			
		||||
int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj,
 | 
			
		||||
			 struct vm_area_struct *vma);
 | 
			
		||||
struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj);
 | 
			
		||||
struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
 | 
			
		||||
			struct dma_buf_attachment *attach, struct sg_table *sg);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										240
									
								
								drivers/gpu/drm/mediatek/mtk_drm_plane.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								drivers/gpu/drm/mediatek/mtk_drm_plane.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,240 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2015 MediaTek Inc.
 | 
			
		||||
 * Author: 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 <drm/drmP.h>
 | 
			
		||||
#include <drm/drm_atomic.h>
 | 
			
		||||
#include <drm/drm_atomic_helper.h>
 | 
			
		||||
#include <drm/drm_plane_helper.h>
 | 
			
		||||
 | 
			
		||||
#include "mtk_drm_crtc.h"
 | 
			
		||||
#include "mtk_drm_ddp_comp.h"
 | 
			
		||||
#include "mtk_drm_drv.h"
 | 
			
		||||
#include "mtk_drm_fb.h"
 | 
			
		||||
#include "mtk_drm_gem.h"
 | 
			
		||||
#include "mtk_drm_plane.h"
 | 
			
		||||
 | 
			
		||||
static const u32 formats[] = {
 | 
			
		||||
	DRM_FORMAT_XRGB8888,
 | 
			
		||||
	DRM_FORMAT_ARGB8888,
 | 
			
		||||
	DRM_FORMAT_RGB565,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable,
 | 
			
		||||
			     dma_addr_t addr, struct drm_rect *dest)
 | 
			
		||||
{
 | 
			
		||||
	struct drm_plane *plane = &mtk_plane->base;
 | 
			
		||||
	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
 | 
			
		||||
	unsigned int pitch, format;
 | 
			
		||||
	int x, y;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(!plane->state || (enable && !plane->state->fb)))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (plane->state->fb) {
 | 
			
		||||
		pitch = plane->state->fb->pitches[0];
 | 
			
		||||
		format = plane->state->fb->pixel_format;
 | 
			
		||||
	} else {
 | 
			
		||||
		pitch = 0;
 | 
			
		||||
		format = DRM_FORMAT_RGBA8888;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	x = plane->state->crtc_x;
 | 
			
		||||
	y = plane->state->crtc_y;
 | 
			
		||||
 | 
			
		||||
	if (x < 0) {
 | 
			
		||||
		addr -= x * 4;
 | 
			
		||||
		x = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (y < 0) {
 | 
			
		||||
		addr -= y * pitch;
 | 
			
		||||
		y = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->pending.enable = enable;
 | 
			
		||||
	state->pending.pitch = pitch;
 | 
			
		||||
	state->pending.format = format;
 | 
			
		||||
	state->pending.addr = addr;
 | 
			
		||||
	state->pending.x = x;
 | 
			
		||||
	state->pending.y = y;
 | 
			
		||||
	state->pending.width = dest->x2 - dest->x1;
 | 
			
		||||
	state->pending.height = dest->y2 - dest->y1;
 | 
			
		||||
	wmb(); /* Make sure the above parameters are set before update */
 | 
			
		||||
	state->pending.dirty = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mtk_plane_reset(struct drm_plane *plane)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_plane_state *state;
 | 
			
		||||
 | 
			
		||||
	if (plane->state) {
 | 
			
		||||
		if (plane->state->fb)
 | 
			
		||||
			drm_framebuffer_unreference(plane->state->fb);
 | 
			
		||||
 | 
			
		||||
		state = to_mtk_plane_state(plane->state);
 | 
			
		||||
		memset(state, 0, sizeof(*state));
 | 
			
		||||
	} else {
 | 
			
		||||
		state = kzalloc(sizeof(*state), GFP_KERNEL);
 | 
			
		||||
		if (!state)
 | 
			
		||||
			return;
 | 
			
		||||
		plane->state = &state->base;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->base.plane = plane;
 | 
			
		||||
	state->pending.format = DRM_FORMAT_RGB565;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
 | 
			
		||||
	struct mtk_plane_state *state;
 | 
			
		||||
 | 
			
		||||
	state = kzalloc(sizeof(*state), GFP_KERNEL);
 | 
			
		||||
	if (!state)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
 | 
			
		||||
 | 
			
		||||
	WARN_ON(state->base.plane != plane);
 | 
			
		||||
 | 
			
		||||
	state->pending = old_state->pending;
 | 
			
		||||
 | 
			
		||||
	return &state->base;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mtk_drm_plane_destroy_state(struct drm_plane *plane,
 | 
			
		||||
					struct drm_plane_state *state)
 | 
			
		||||
{
 | 
			
		||||
	__drm_atomic_helper_plane_destroy_state(plane, state);
 | 
			
		||||
	kfree(to_mtk_plane_state(state));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct drm_plane_funcs mtk_plane_funcs = {
 | 
			
		||||
	.update_plane = drm_atomic_helper_update_plane,
 | 
			
		||||
	.disable_plane = drm_atomic_helper_disable_plane,
 | 
			
		||||
	.destroy = drm_plane_cleanup,
 | 
			
		||||
	.reset = mtk_plane_reset,
 | 
			
		||||
	.atomic_duplicate_state = mtk_plane_duplicate_state,
 | 
			
		||||
	.atomic_destroy_state = mtk_drm_plane_destroy_state,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtk_plane_atomic_check(struct drm_plane *plane,
 | 
			
		||||
				  struct drm_plane_state *state)
 | 
			
		||||
{
 | 
			
		||||
	struct drm_framebuffer *fb = state->fb;
 | 
			
		||||
	struct drm_crtc_state *crtc_state;
 | 
			
		||||
	bool visible;
 | 
			
		||||
	struct drm_rect dest = {
 | 
			
		||||
		.x1 = state->crtc_x,
 | 
			
		||||
		.y1 = state->crtc_y,
 | 
			
		||||
		.x2 = state->crtc_x + state->crtc_w,
 | 
			
		||||
		.y2 = state->crtc_y + state->crtc_h,
 | 
			
		||||
	};
 | 
			
		||||
	struct drm_rect src = {
 | 
			
		||||
		/* 16.16 fixed point */
 | 
			
		||||
		.x1 = state->src_x,
 | 
			
		||||
		.y1 = state->src_y,
 | 
			
		||||
		.x2 = state->src_x + state->src_w,
 | 
			
		||||
		.y2 = state->src_y + state->src_h,
 | 
			
		||||
	};
 | 
			
		||||
	struct drm_rect clip = { 0, };
 | 
			
		||||
 | 
			
		||||
	if (!fb)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!mtk_fb_get_gem_obj(fb)) {
 | 
			
		||||
		DRM_DEBUG_KMS("buffer is null\n");
 | 
			
		||||
		return -EFAULT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!state->crtc)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
 | 
			
		||||
	if (IS_ERR(crtc_state))
 | 
			
		||||
		return PTR_ERR(crtc_state);
 | 
			
		||||
 | 
			
		||||
	clip.x2 = crtc_state->mode.hdisplay;
 | 
			
		||||
	clip.y2 = crtc_state->mode.vdisplay;
 | 
			
		||||
 | 
			
		||||
	return drm_plane_helper_check_update(plane, state->crtc, fb,
 | 
			
		||||
					     &src, &dest, &clip,
 | 
			
		||||
					     DRM_PLANE_HELPER_NO_SCALING,
 | 
			
		||||
					     DRM_PLANE_HELPER_NO_SCALING,
 | 
			
		||||
					     true, true, &visible);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mtk_plane_atomic_update(struct drm_plane *plane,
 | 
			
		||||
				    struct drm_plane_state *old_state)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
 | 
			
		||||
	struct drm_crtc *crtc = state->base.crtc;
 | 
			
		||||
	struct drm_gem_object *gem;
 | 
			
		||||
	struct mtk_drm_gem_obj *mtk_gem;
 | 
			
		||||
	struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
 | 
			
		||||
	struct drm_rect dest = {
 | 
			
		||||
		.x1 = state->base.crtc_x,
 | 
			
		||||
		.y1 = state->base.crtc_y,
 | 
			
		||||
		.x2 = state->base.crtc_x + state->base.crtc_w,
 | 
			
		||||
		.y2 = state->base.crtc_y + state->base.crtc_h,
 | 
			
		||||
	};
 | 
			
		||||
	struct drm_rect clip = { 0, };
 | 
			
		||||
 | 
			
		||||
	if (!crtc)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	clip.x2 = state->base.crtc->state->mode.hdisplay;
 | 
			
		||||
	clip.y2 = state->base.crtc->state->mode.vdisplay;
 | 
			
		||||
	drm_rect_intersect(&dest, &clip);
 | 
			
		||||
 | 
			
		||||
	gem = mtk_fb_get_gem_obj(state->base.fb);
 | 
			
		||||
	mtk_gem = to_mtk_gem_obj(gem);
 | 
			
		||||
	mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mtk_plane_atomic_disable(struct drm_plane *plane,
 | 
			
		||||
				     struct drm_plane_state *old_state)
 | 
			
		||||
{
 | 
			
		||||
	struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
 | 
			
		||||
 | 
			
		||||
	state->pending.enable = false;
 | 
			
		||||
	wmb(); /* Make sure the above parameter is set before update */
 | 
			
		||||
	state->pending.dirty = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
 | 
			
		||||
	.atomic_check = mtk_plane_atomic_check,
 | 
			
		||||
	.atomic_update = mtk_plane_atomic_update,
 | 
			
		||||
	.atomic_disable = mtk_plane_atomic_disable,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
 | 
			
		||||
		   unsigned long possible_crtcs, enum drm_plane_type type,
 | 
			
		||||
		   unsigned int zpos)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
 | 
			
		||||
				       &mtk_plane_funcs, formats,
 | 
			
		||||
				       ARRAY_SIZE(formats), type, NULL);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		DRM_ERROR("failed to initialize plane\n");
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs);
 | 
			
		||||
	mtk_plane->idx = zpos;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								drivers/gpu/drm/mediatek/mtk_drm_plane.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								drivers/gpu/drm/mediatek/mtk_drm_plane.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2015 MediaTek Inc.
 | 
			
		||||
 * Author: 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _MTK_DRM_PLANE_H_
 | 
			
		||||
#define _MTK_DRM_PLANE_H_
 | 
			
		||||
 | 
			
		||||
#include <drm/drm_crtc.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
struct mtk_drm_plane {
 | 
			
		||||
	struct drm_plane		base;
 | 
			
		||||
	unsigned int			idx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mtk_plane_pending_state {
 | 
			
		||||
	bool				config;
 | 
			
		||||
	bool				enable;
 | 
			
		||||
	dma_addr_t			addr;
 | 
			
		||||
	unsigned int			pitch;
 | 
			
		||||
	unsigned int			format;
 | 
			
		||||
	unsigned int			x;
 | 
			
		||||
	unsigned int			y;
 | 
			
		||||
	unsigned int			width;
 | 
			
		||||
	unsigned int			height;
 | 
			
		||||
	bool				dirty;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mtk_plane_state {
 | 
			
		||||
	struct drm_plane_state		base;
 | 
			
		||||
	struct mtk_plane_pending_state	pending;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(plane, struct mtk_drm_plane, base);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct mtk_plane_state *
 | 
			
		||||
to_mtk_plane_state(struct drm_plane_state *state)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(state, struct mtk_plane_state, base);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
 | 
			
		||||
		   unsigned long possible_crtcs, enum drm_plane_type type,
 | 
			
		||||
		   unsigned int zpos);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
		Loading…
	
		Reference in a new issue