You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08: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:
@@ -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
File diff suppressed because it is too large
Load Diff
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
File diff suppressed because it is too large
Load Diff
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
|
||||
Reference in New Issue
Block a user