Skip to content

Commit 119f517

Browse files
ckhu-mediatekpH5
authored andcommitted
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 <[email protected]> Signed-off-by: YT Shen <[email protected]> Signed-off-by: Daniel Kurtz <[email protected]> Signed-off-by: Bibby Hsieh <[email protected]> Signed-off-by: Mao Huang <[email protected]> Signed-off-by: Philipp Zabel <[email protected]>
1 parent 923dd88 commit 119f517

20 files changed

+3389
-0
lines changed

drivers/gpu/drm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,5 @@ source "drivers/gpu/drm/etnaviv/Kconfig"
288288
source "drivers/gpu/drm/arc/Kconfig"
289289

290290
source "drivers/gpu/drm/hisilicon/Kconfig"
291+
292+
source "drivers/gpu/drm/mediatek/Kconfig"

drivers/gpu/drm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_MSM) += msm/
7474
obj-$(CONFIG_DRM_TEGRA) += tegra/
7575
obj-$(CONFIG_DRM_STI) += sti/
7676
obj-$(CONFIG_DRM_IMX) += imx/
77+
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
7778
obj-y += i2c/
7879
obj-y += panel/
7980
obj-y += bridge/

drivers/gpu/drm/mediatek/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
config DRM_MEDIATEK
2+
tristate "DRM Support for Mediatek SoCs"
3+
depends on DRM
4+
depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
5+
select DRM_GEM_CMA_HELPER
6+
select DRM_KMS_HELPER
7+
select IOMMU_DMA
8+
select MEMORY
9+
select MTK_SMI
10+
help
11+
Choose this option if you have a Mediatek SoCs.
12+
The module will be called mediatek-drm
13+
This driver provides kernel mode setting and
14+
buffer management to userspace.

drivers/gpu/drm/mediatek/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
mediatek-drm-y := mtk_disp_ovl.o \
2+
mtk_disp_rdma.o \
3+
mtk_drm_crtc.o \
4+
mtk_drm_ddp.o \
5+
mtk_drm_ddp_comp.o \
6+
mtk_drm_drv.o \
7+
mtk_drm_fb.o \
8+
mtk_drm_gem.o \
9+
mtk_drm_plane.o
10+
11+
obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
/*
2+
* Copyright (c) 2015 MediaTek Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License version 2 as
6+
* published by the Free Software Foundation.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* GNU General Public License for more details.
12+
*/
13+
14+
#include <drm/drmP.h>
15+
#include <linux/clk.h>
16+
#include <linux/component.h>
17+
#include <linux/of_device.h>
18+
#include <linux/of_irq.h>
19+
#include <linux/platform_device.h>
20+
21+
#include "mtk_drm_crtc.h"
22+
#include "mtk_drm_ddp_comp.h"
23+
24+
#define DISP_REG_OVL_INTEN 0x0004
25+
#define OVL_FME_CPL_INT BIT(1)
26+
#define DISP_REG_OVL_INTSTA 0x0008
27+
#define DISP_REG_OVL_EN 0x000c
28+
#define DISP_REG_OVL_RST 0x0014
29+
#define DISP_REG_OVL_ROI_SIZE 0x0020
30+
#define DISP_REG_OVL_ROI_BGCLR 0x0028
31+
#define DISP_REG_OVL_SRC_CON 0x002c
32+
#define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n))
33+
#define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n))
34+
#define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n))
35+
#define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n))
36+
#define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n))
37+
#define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n))
38+
#define DISP_REG_OVL_ADDR(n) (0x0f40 + 0x20 * (n))
39+
40+
#define OVL_RDMA_MEM_GMC 0x40402020
41+
42+
#define OVL_CON_BYTE_SWAP BIT(24)
43+
#define OVL_CON_CLRFMT_RGB565 (0 << 12)
44+
#define OVL_CON_CLRFMT_RGB888 (1 << 12)
45+
#define OVL_CON_CLRFMT_RGBA8888 (2 << 12)
46+
#define OVL_CON_CLRFMT_ARGB8888 (3 << 12)
47+
#define OVL_CON_AEN BIT(8)
48+
#define OVL_CON_ALPHA 0xff
49+
50+
/**
51+
* struct mtk_disp_ovl - DISP_OVL driver structure
52+
* @ddp_comp - structure containing type enum and hardware resources
53+
* @crtc - associated crtc to report vblank events to
54+
*/
55+
struct mtk_disp_ovl {
56+
struct mtk_ddp_comp ddp_comp;
57+
struct drm_crtc *crtc;
58+
};
59+
60+
static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id)
61+
{
62+
struct mtk_disp_ovl *priv = dev_id;
63+
struct mtk_ddp_comp *ovl = &priv->ddp_comp;
64+
65+
/* Clear frame completion interrupt */
66+
writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA);
67+
68+
if (!priv->crtc)
69+
return IRQ_NONE;
70+
71+
mtk_crtc_ddp_irq(priv->crtc, ovl);
72+
73+
return IRQ_HANDLED;
74+
}
75+
76+
static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp,
77+
struct drm_crtc *crtc)
78+
{
79+
struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
80+
ddp_comp);
81+
82+
priv->crtc = crtc;
83+
writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN);
84+
}
85+
86+
static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp)
87+
{
88+
struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl,
89+
ddp_comp);
90+
91+
priv->crtc = NULL;
92+
writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN);
93+
}
94+
95+
static void mtk_ovl_start(struct mtk_ddp_comp *comp)
96+
{
97+
writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
98+
}
99+
100+
static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
101+
{
102+
writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
103+
}
104+
105+
static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
106+
unsigned int h, unsigned int vrefresh)
107+
{
108+
if (w != 0 && h != 0)
109+
writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
110+
writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR);
111+
112+
writel(0x1, comp->regs + DISP_REG_OVL_RST);
113+
writel(0x0, comp->regs + DISP_REG_OVL_RST);
114+
}
115+
116+
static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx)
117+
{
118+
unsigned int reg;
119+
120+
writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
121+
writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx));
122+
123+
reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
124+
reg = reg | BIT(idx);
125+
writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
126+
}
127+
128+
static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
129+
{
130+
unsigned int reg;
131+
132+
reg = readl(comp->regs + DISP_REG_OVL_SRC_CON);
133+
reg = reg & ~BIT(idx);
134+
writel(reg, comp->regs + DISP_REG_OVL_SRC_CON);
135+
136+
writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
137+
}
138+
139+
static unsigned int ovl_fmt_convert(unsigned int fmt)
140+
{
141+
switch (fmt) {
142+
default:
143+
case DRM_FORMAT_RGB565:
144+
return OVL_CON_CLRFMT_RGB565;
145+
case DRM_FORMAT_BGR565:
146+
return OVL_CON_CLRFMT_RGB565 | OVL_CON_BYTE_SWAP;
147+
case DRM_FORMAT_RGB888:
148+
return OVL_CON_CLRFMT_RGB888;
149+
case DRM_FORMAT_BGR888:
150+
return OVL_CON_CLRFMT_RGB888 | OVL_CON_BYTE_SWAP;
151+
case DRM_FORMAT_RGBX8888:
152+
case DRM_FORMAT_RGBA8888:
153+
return OVL_CON_CLRFMT_ARGB8888;
154+
case DRM_FORMAT_BGRX8888:
155+
case DRM_FORMAT_BGRA8888:
156+
return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP;
157+
case DRM_FORMAT_XRGB8888:
158+
case DRM_FORMAT_ARGB8888:
159+
return OVL_CON_CLRFMT_RGBA8888;
160+
case DRM_FORMAT_XBGR8888:
161+
case DRM_FORMAT_ABGR8888:
162+
return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
163+
}
164+
}
165+
166+
static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
167+
struct mtk_plane_state *state)
168+
{
169+
struct mtk_plane_pending_state *pending = &state->pending;
170+
unsigned int addr = pending->addr;
171+
unsigned int pitch = pending->pitch & 0xffff;
172+
unsigned int fmt = pending->format;
173+
unsigned int offset = (pending->y << 16) | pending->x;
174+
unsigned int src_size = (pending->height << 16) | pending->width;
175+
unsigned int con;
176+
177+
if (!pending->enable)
178+
mtk_ovl_layer_off(comp, idx);
179+
180+
con = ovl_fmt_convert(fmt);
181+
if (idx != 0)
182+
con |= OVL_CON_AEN | OVL_CON_ALPHA;
183+
184+
writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
185+
writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
186+
writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
187+
writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
188+
writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(idx));
189+
190+
if (pending->enable)
191+
mtk_ovl_layer_on(comp, idx);
192+
}
193+
194+
static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = {
195+
.config = mtk_ovl_config,
196+
.start = mtk_ovl_start,
197+
.stop = mtk_ovl_stop,
198+
.enable_vblank = mtk_ovl_enable_vblank,
199+
.disable_vblank = mtk_ovl_disable_vblank,
200+
.layer_on = mtk_ovl_layer_on,
201+
.layer_off = mtk_ovl_layer_off,
202+
.layer_config = mtk_ovl_layer_config,
203+
};
204+
205+
static int mtk_disp_ovl_bind(struct device *dev, struct device *master,
206+
void *data)
207+
{
208+
struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
209+
struct drm_device *drm_dev = data;
210+
int ret;
211+
212+
ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
213+
if (ret < 0) {
214+
dev_err(dev, "Failed to register component %s: %d\n",
215+
dev->of_node->full_name, ret);
216+
return ret;
217+
}
218+
219+
return 0;
220+
}
221+
222+
static void mtk_disp_ovl_unbind(struct device *dev, struct device *master,
223+
void *data)
224+
{
225+
struct mtk_disp_ovl *priv = dev_get_drvdata(dev);
226+
struct drm_device *drm_dev = data;
227+
228+
mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
229+
}
230+
231+
static const struct component_ops mtk_disp_ovl_component_ops = {
232+
.bind = mtk_disp_ovl_bind,
233+
.unbind = mtk_disp_ovl_unbind,
234+
};
235+
236+
static int mtk_disp_ovl_probe(struct platform_device *pdev)
237+
{
238+
struct device *dev = &pdev->dev;
239+
struct mtk_disp_ovl *priv;
240+
int comp_id;
241+
int irq;
242+
int ret;
243+
244+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
245+
if (!priv)
246+
return -ENOMEM;
247+
248+
irq = platform_get_irq(pdev, 0);
249+
if (irq < 0)
250+
return irq;
251+
252+
ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler,
253+
IRQF_TRIGGER_NONE, dev_name(dev), priv);
254+
if (ret < 0) {
255+
dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
256+
return ret;
257+
}
258+
259+
comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL);
260+
if (comp_id < 0) {
261+
dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
262+
return comp_id;
263+
}
264+
265+
ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
266+
&mtk_disp_ovl_funcs);
267+
if (ret) {
268+
dev_err(dev, "Failed to initialize component: %d\n", ret);
269+
return ret;
270+
}
271+
272+
platform_set_drvdata(pdev, priv);
273+
274+
ret = component_add(dev, &mtk_disp_ovl_component_ops);
275+
if (ret)
276+
dev_err(dev, "Failed to add component: %d\n", ret);
277+
278+
return ret;
279+
}
280+
281+
static int mtk_disp_ovl_remove(struct platform_device *pdev)
282+
{
283+
component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
284+
285+
return 0;
286+
}
287+
288+
static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
289+
{ .compatible = "mediatek,mt8173-disp-ovl", },
290+
{},
291+
};
292+
MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
293+
294+
struct platform_driver mtk_disp_ovl_driver = {
295+
.probe = mtk_disp_ovl_probe,
296+
.remove = mtk_disp_ovl_remove,
297+
.driver = {
298+
.name = "mediatek-disp-ovl",
299+
.owner = THIS_MODULE,
300+
.of_match_table = mtk_disp_ovl_driver_dt_match,
301+
},
302+
};

0 commit comments

Comments
 (0)