aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCK Hu <ck.hu@mediatek.com>2016-01-04 12:36:34 -0500
committerPhilipp Zabel <p.zabel@pengutronix.de>2016-05-06 11:47:35 -0400
commit119f5173628aa7a0c3cf9db83460d40709e8241d (patch)
treeed062fe93a24bcf0eb17a568fe889558c76e4a5c
parent923dd88d3cf87ed3751d3ce8a8abc43fdbb4632b (diff)
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>
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig14
-rw-r--r--drivers/gpu/drm/mediatek/Makefile11
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl.c302
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_rdma.c240
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.c582
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.h32
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp.c353
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp.h41
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c225
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h150
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c564
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.h57
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_fb.c165
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_fb.h23
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.c269
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.h59
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.c240
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.h59
20 files changed, 3389 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 16e4c2135aad..fc357319de35 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -288,3 +288,5 @@ source "drivers/gpu/drm/etnaviv/Kconfig"
288source "drivers/gpu/drm/arc/Kconfig" 288source "drivers/gpu/drm/arc/Kconfig"
289 289
290source "drivers/gpu/drm/hisilicon/Kconfig" 290source "drivers/gpu/drm/hisilicon/Kconfig"
291
292source "drivers/gpu/drm/mediatek/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 43c2abf425ee..2bd3e5aa43c6 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_MSM) += msm/
74obj-$(CONFIG_DRM_TEGRA) += tegra/ 74obj-$(CONFIG_DRM_TEGRA) += tegra/
75obj-$(CONFIG_DRM_STI) += sti/ 75obj-$(CONFIG_DRM_STI) += sti/
76obj-$(CONFIG_DRM_IMX) += imx/ 76obj-$(CONFIG_DRM_IMX) += imx/
77obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
77obj-y += i2c/ 78obj-y += i2c/
78obj-y += panel/ 79obj-y += panel/
79obj-y += bridge/ 80obj-y += bridge/
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
new file mode 100644
index 000000000000..30e5371b56e8
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -0,0 +1,14 @@
1config 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.
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
new file mode 100644
index 000000000000..d4bde7c13c8e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -0,0 +1,11 @@
1mediatek-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
11obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
new file mode 100644
index 000000000000..8f62671fcfbf
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -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 */
55struct mtk_disp_ovl {
56 struct mtk_ddp_comp ddp_comp;
57 struct drm_crtc *crtc;
58};
59
60static 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
76static 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
86static 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
95static void mtk_ovl_start(struct mtk_ddp_comp *comp)
96{
97 writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
98}
99
100static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
101{
102 writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
103}
104
105static 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
116static 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
128static 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
139static 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
166static 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
194static 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
205static 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
222static 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
231static const struct component_ops mtk_disp_ovl_component_ops = {
232 .bind = mtk_disp_ovl_bind,
233 .unbind = mtk_disp_ovl_unbind,
234};
235
236static 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
281static 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
288static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
289 { .compatible = "mediatek,mt8173-disp-ovl", },
290 {},
291};
292MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
293
294struct 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};
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
new file mode 100644
index 000000000000..5fb80cbe4c5b
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -0,0 +1,240 @@
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_RDMA_INT_ENABLE 0x0000
25#define DISP_REG_RDMA_INT_STATUS 0x0004
26#define RDMA_TARGET_LINE_INT BIT(5)
27#define RDMA_FIFO_UNDERFLOW_INT BIT(4)
28#define RDMA_EOF_ABNORMAL_INT BIT(3)
29#define RDMA_FRAME_END_INT BIT(2)
30#define RDMA_FRAME_START_INT BIT(1)
31#define RDMA_REG_UPDATE_INT BIT(0)
32#define DISP_REG_RDMA_GLOBAL_CON 0x0010
33#define RDMA_ENGINE_EN BIT(0)
34#define DISP_REG_RDMA_SIZE_CON_0 0x0014
35#define DISP_REG_RDMA_SIZE_CON_1 0x0018
36#define DISP_REG_RDMA_TARGET_LINE 0x001c
37#define DISP_REG_RDMA_FIFO_CON 0x0040
38#define RDMA_FIFO_UNDERFLOW_EN BIT(31)
39#define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16)
40#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16)
41
42/**
43 * struct mtk_disp_rdma - DISP_RDMA driver structure
44 * @ddp_comp - structure containing type enum and hardware resources
45 * @crtc - associated crtc to report irq events to
46 */
47struct mtk_disp_rdma {
48 struct mtk_ddp_comp ddp_comp;
49 struct drm_crtc *crtc;
50};
51
52static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id)
53{
54 struct mtk_disp_rdma *priv = dev_id;
55 struct mtk_ddp_comp *rdma = &priv->ddp_comp;
56
57 /* Clear frame completion interrupt */
58 writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS);
59
60 if (!priv->crtc)
61 return IRQ_NONE;
62
63 mtk_crtc_ddp_irq(priv->crtc, rdma);
64
65 return IRQ_HANDLED;
66}
67
68static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg,
69 unsigned int mask, unsigned int val)
70{
71 unsigned int tmp = readl(comp->regs + reg);
72
73 tmp = (tmp & ~mask) | (val & mask);
74 writel(tmp, comp->regs + reg);
75}
76
77static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp,
78 struct drm_crtc *crtc)
79{
80 struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
81 ddp_comp);
82
83 priv->crtc = crtc;
84 rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT,
85 RDMA_FRAME_END_INT);
86}
87
88static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp)
89{
90 struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma,
91 ddp_comp);
92
93 priv->crtc = NULL;
94 rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0);
95}
96
97static void mtk_rdma_start(struct mtk_ddp_comp *comp)
98{
99 rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN,
100 RDMA_ENGINE_EN);
101}
102
103static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
104{
105 rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
106}
107
108static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
109 unsigned int height, unsigned int vrefresh)
110{
111 unsigned int threshold;
112 unsigned int reg;
113
114 rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width);
115 rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height);
116
117 /*
118 * Enable FIFO underflow since DSI and DPI can't be blocked.
119 * Keep the FIFO pseudo size reset default of 8 KiB. Set the
120 * output threshold to 6 microseconds with 7/6 overhead to
121 * account for blanking, and with a pixel depth of 4 bytes:
122 */
123 threshold = width * height * vrefresh * 4 * 7 / 1000000;
124 reg = RDMA_FIFO_UNDERFLOW_EN |
125 RDMA_FIFO_PSEUDO_SIZE(SZ_8K) |
126 RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
127 writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
128}
129
130static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = {
131 .config = mtk_rdma_config,
132 .start = mtk_rdma_start,
133 .stop = mtk_rdma_stop,
134 .enable_vblank = mtk_rdma_enable_vblank,
135 .disable_vblank = mtk_rdma_disable_vblank,
136};
137
138static int mtk_disp_rdma_bind(struct device *dev, struct device *master,
139 void *data)
140{
141 struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
142 struct drm_device *drm_dev = data;
143 int ret;
144
145 ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
146 if (ret < 0) {
147 dev_err(dev, "Failed to register component %s: %d\n",
148 dev->of_node->full_name, ret);
149 return ret;
150 }
151
152 return 0;
153
154}
155
156static void mtk_disp_rdma_unbind(struct device *dev, struct device *master,
157 void *data)
158{
159 struct mtk_disp_rdma *priv = dev_get_drvdata(dev);
160 struct drm_device *drm_dev = data;
161
162 mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
163}
164
165static const struct component_ops mtk_disp_rdma_component_ops = {
166 .bind = mtk_disp_rdma_bind,
167 .unbind = mtk_disp_rdma_unbind,
168};
169
170static int mtk_disp_rdma_probe(struct platform_device *pdev)
171{
172 struct device *dev = &pdev->dev;
173 struct mtk_disp_rdma *priv;
174 int comp_id;
175 int irq;
176 int ret;
177
178 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
179 if (!priv)
180 return -ENOMEM;
181
182 irq = platform_get_irq(pdev, 0);
183 if (irq < 0)
184 return irq;
185
186 comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA);
187 if (comp_id < 0) {
188 dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
189 return comp_id;
190 }
191
192 ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
193 &mtk_disp_rdma_funcs);
194 if (ret) {
195 dev_err(dev, "Failed to initialize component: %d\n", ret);
196 return ret;
197 }
198
199 /* Disable and clear pending interrupts */
200 writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE);
201 writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS);
202
203 ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler,
204 IRQF_TRIGGER_NONE, dev_name(dev), priv);
205 if (ret < 0) {
206 dev_err(dev, "Failed to request irq %d: %d\n", irq, ret);
207 return ret;
208 }
209
210 platform_set_drvdata(pdev, priv);
211
212 ret = component_add(dev, &mtk_disp_rdma_component_ops);
213 if (ret)
214 dev_err(dev, "Failed to add component: %d\n", ret);
215
216 return ret;
217}
218
219static int mtk_disp_rdma_remove(struct platform_device *pdev)
220{
221 component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
222
223 return 0;
224}
225
226static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
227 { .compatible = "mediatek,mt8173-disp-rdma", },
228 {},
229};
230MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
231
232struct platform_driver mtk_disp_rdma_driver = {
233 .probe = mtk_disp_rdma_probe,
234 .remove = mtk_disp_rdma_remove,
235 .driver = {
236 .name = "mediatek-disp-rdma",
237 .owner = THIS_MODULE,
238 .of_match_table = mtk_disp_rdma_driver_dt_match,
239 },
240};
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
new file mode 100644
index 000000000000..3095fc182f07
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -0,0 +1,582 @@
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 <asm/barrier.h>
15#include <drm/drmP.h>
16#include <drm/drm_atomic_helper.h>
17#include <drm/drm_crtc_helper.h>
18#include <drm/drm_plane_helper.h>
19#include <linux/clk.h>
20#include <linux/pm_runtime.h>
21#include <soc/mediatek/smi.h>
22
23#include "mtk_drm_drv.h"
24#include "mtk_drm_crtc.h"
25#include "mtk_drm_ddp.h"
26#include "mtk_drm_ddp_comp.h"
27#include "mtk_drm_gem.h"
28#include "mtk_drm_plane.h"
29
30/**
31 * struct mtk_drm_crtc - MediaTek specific crtc structure.
32 * @base: crtc object.
33 * @enabled: records whether crtc_enable succeeded
34 * @planes: array of 4 mtk_drm_plane structures, one for each overlay plane
35 * @pending_planes: whether any plane has pending changes to be applied
36 * @config_regs: memory mapped mmsys configuration register space
37 * @mutex: handle to one of the ten disp_mutex streams
38 * @ddp_comp_nr: number of components in ddp_comp
39 * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
40 */
41struct mtk_drm_crtc {
42 struct drm_crtc base;
43 bool enabled;
44
45 bool pending_needs_vblank;
46 struct drm_pending_vblank_event *event;
47
48 struct mtk_drm_plane planes[OVL_LAYER_NR];
49 bool pending_planes;
50
51 void __iomem *config_regs;
52 struct mtk_disp_mutex *mutex;
53 unsigned int ddp_comp_nr;
54 struct mtk_ddp_comp **ddp_comp;
55};
56
57struct mtk_crtc_state {
58 struct drm_crtc_state base;
59
60 bool pending_config;
61 unsigned int pending_width;
62 unsigned int pending_height;
63 unsigned int pending_vrefresh;
64};
65
66static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c)
67{
68 return container_of(c, struct mtk_drm_crtc, base);
69}
70
71static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s)
72{
73 return container_of(s, struct mtk_crtc_state, base);
74}
75
76static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
77{
78 struct drm_crtc *crtc = &mtk_crtc->base;
79 unsigned long flags;
80
81 spin_lock_irqsave(&crtc->dev->event_lock, flags);
82 drm_crtc_send_vblank_event(crtc, mtk_crtc->event);
83 drm_crtc_vblank_put(crtc);
84 mtk_crtc->event = NULL;
85 spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
86}
87
88static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
89{
90 drm_crtc_handle_vblank(&mtk_crtc->base);
91 if (mtk_crtc->pending_needs_vblank) {
92 mtk_drm_crtc_finish_page_flip(mtk_crtc);
93 mtk_crtc->pending_needs_vblank = false;
94 }
95}
96
97static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
98{
99 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
100 int i;
101
102 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
103 clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
104
105 mtk_disp_mutex_put(mtk_crtc->mutex);
106
107 drm_crtc_cleanup(crtc);
108}
109
110static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
111{
112 struct mtk_crtc_state *state;
113
114 if (crtc->state) {
115 if (crtc->state->mode_blob)
116 drm_property_unreference_blob(crtc->state->mode_blob);
117
118 state = to_mtk_crtc_state(crtc->state);
119 memset(state, 0, sizeof(*state));
120 } else {
121 state = kzalloc(sizeof(*state), GFP_KERNEL);
122 if (!state)
123 return;
124 crtc->state = &state->base;
125 }
126
127 state->base.crtc = crtc;
128}
129
130static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
131{
132 struct mtk_crtc_state *state;
133
134 state = kzalloc(sizeof(*state), GFP_KERNEL);
135 if (!state)
136 return NULL;
137
138 __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
139
140 WARN_ON(state->base.crtc != crtc);
141 state->base.crtc = crtc;
142
143 return &state->base;
144}
145
146static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc,
147 struct drm_crtc_state *state)
148{
149 __drm_atomic_helper_crtc_destroy_state(crtc, state);
150 kfree(to_mtk_crtc_state(state));
151}
152
153static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
154 const struct drm_display_mode *mode,
155 struct drm_display_mode *adjusted_mode)
156{
157 /* Nothing to do here, but this callback is mandatory. */
158 return true;
159}
160
161static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
162{
163 struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
164
165 state->pending_width = crtc->mode.hdisplay;
166 state->pending_height = crtc->mode.vdisplay;
167 state->pending_vrefresh = crtc->mode.vrefresh;
168 wmb(); /* Make sure the above parameters are set before update */
169 state->pending_config = true;
170}
171
172int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
173{
174 struct mtk_drm_private *priv = drm->dev_private;
175 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
176 struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
177
178 mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base);
179
180 return 0;
181}
182
183void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
184{
185 struct mtk_drm_private *priv = drm->dev_private;
186 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
187 struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
188
189 mtk_ddp_comp_disable_vblank(ovl);
190}
191
192static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc)
193{
194 int ret;
195 int i;
196
197 DRM_DEBUG_DRIVER("%s\n", __func__);
198 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
199 ret = clk_enable(mtk_crtc->ddp_comp[i]->clk);
200 if (ret) {
201 DRM_ERROR("Failed to enable clock %d: %d\n", i, ret);
202 goto err;
203 }
204 }
205
206 return 0;
207err:
208 while (--i >= 0)
209 clk_disable(mtk_crtc->ddp_comp[i]->clk);
210 return ret;
211}
212
213static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
214{
215 int i;
216
217 DRM_DEBUG_DRIVER("%s\n", __func__);
218 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
219 clk_disable(mtk_crtc->ddp_comp[i]->clk);
220}
221
222static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
223{
224 struct drm_crtc *crtc = &mtk_crtc->base;
225 unsigned int width, height, vrefresh;
226 int ret;
227 int i;
228
229 DRM_DEBUG_DRIVER("%s\n", __func__);
230 if (WARN_ON(!crtc->state))
231 return -EINVAL;
232
233 width = crtc->state->adjusted_mode.hdisplay;
234 height = crtc->state->adjusted_mode.vdisplay;
235 vrefresh = crtc->state->adjusted_mode.vrefresh;
236
237 ret = pm_runtime_get_sync(crtc->dev->dev);
238 if (ret < 0) {
239 DRM_ERROR("Failed to enable power domain: %d\n", ret);
240 return ret;
241 }
242
243 ret = mtk_disp_mutex_prepare(mtk_crtc->mutex);
244 if (ret < 0) {
245 DRM_ERROR("Failed to enable mutex clock: %d\n", ret);
246 goto err_pm_runtime_put;
247 }
248
249 ret = mtk_crtc_ddp_clk_enable(mtk_crtc);
250 if (ret < 0) {
251 DRM_ERROR("Failed to enable component clocks: %d\n", ret);
252 goto err_mutex_unprepare;
253 }
254
255 DRM_DEBUG_DRIVER("mediatek_ddp_ddp_path_setup\n");
256 for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
257 mtk_ddp_add_comp_to_path(mtk_crtc->config_regs,
258 mtk_crtc->ddp_comp[i]->id,
259 mtk_crtc->ddp_comp[i + 1]->id);
260 mtk_disp_mutex_add_comp(mtk_crtc->mutex,
261 mtk_crtc->ddp_comp[i]->id);
262 }
263 mtk_disp_mutex_add_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
264 mtk_disp_mutex_enable(mtk_crtc->mutex);
265
266 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
267 struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
268
269 mtk_ddp_comp_config(comp, width, height, vrefresh);
270 mtk_ddp_comp_start(comp);
271 }
272
273 /* Initially configure all planes */
274 for (i = 0; i < OVL_LAYER_NR; i++) {
275 struct drm_plane *plane = &mtk_crtc->planes[i].base;
276 struct mtk_plane_state *plane_state;
277
278 plane_state = to_mtk_plane_state(plane->state);
279 mtk_ddp_comp_layer_config(mtk_crtc->ddp_comp[0], i,
280 plane_state);
281 }
282
283 return 0;
284
285err_mutex_unprepare:
286 mtk_disp_mutex_unprepare(mtk_crtc->mutex);
287err_pm_runtime_put:
288 pm_runtime_put(crtc->dev->dev);
289 return ret;
290}
291
292static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
293{
294 struct drm_device *drm = mtk_crtc->base.dev;
295 int i;
296
297 DRM_DEBUG_DRIVER("%s\n", __func__);
298 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
299 mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]);
300 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
301 mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
302 mtk_crtc->ddp_comp[i]->id);
303 mtk_disp_mutex_disable(mtk_crtc->mutex);
304 for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
305 mtk_ddp_remove_comp_from_path(mtk_crtc->config_regs,
306 mtk_crtc->ddp_comp[i]->id,
307 mtk_crtc->ddp_comp[i + 1]->id);
308 mtk_disp_mutex_remove_comp(mtk_crtc->mutex,
309 mtk_crtc->ddp_comp[i]->id);
310 }
311 mtk_disp_mutex_remove_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
312 mtk_crtc_ddp_clk_disable(mtk_crtc);
313 mtk_disp_mutex_unprepare(mtk_crtc->mutex);
314
315 pm_runtime_put(drm->dev);
316}
317
318static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
319{
320 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
321 struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
322 int ret;
323
324 DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
325
326 ret = mtk_smi_larb_get(ovl->larb_dev);
327 if (ret) {
328 DRM_ERROR("Failed to get larb: %d\n", ret);
329 return;
330 }
331
332 ret = mtk_crtc_ddp_hw_init(mtk_crtc);
333 if (ret) {
334 mtk_smi_larb_put(ovl->larb_dev);
335 return;
336 }
337
338 drm_crtc_vblank_on(crtc);
339 mtk_crtc->enabled = true;
340}
341
342static void mtk_drm_crtc_disable(struct drm_crtc *crtc)
343{
344 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
345 struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
346 int i;
347
348 DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
349 if (!mtk_crtc->enabled)
350 return;
351
352 /* Set all pending plane state to disabled */
353 for (i = 0; i < OVL_LAYER_NR; i++) {
354 struct drm_plane *plane = &mtk_crtc->planes[i].base;
355 struct mtk_plane_state *plane_state;
356
357 plane_state = to_mtk_plane_state(plane->state);
358 plane_state->pending.enable = false;
359 plane_state->pending.config = true;
360 }
361 mtk_crtc->pending_planes = true;
362
363 /* Wait for planes to be disabled */
364 drm_crtc_wait_one_vblank(crtc);
365
366 drm_crtc_vblank_off(crtc);
367 mtk_crtc_ddp_hw_fini(mtk_crtc);
368 mtk_smi_larb_put(ovl->larb_dev);
369
370 mtk_crtc->enabled = false;
371}
372
373static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
374 struct drm_crtc_state *old_crtc_state)
375{
376 struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
377 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
378
379 if (mtk_crtc->event && state->base.event)
380 DRM_ERROR("new event while there is still a pending event\n");
381
382 if (state->base.event) {
383 state->base.event->pipe = drm_crtc_index(crtc);
384 WARN_ON(drm_crtc_vblank_get(crtc) != 0);
385 mtk_crtc->event = state->base.event;
386 state->base.event = NULL;
387 }
388}
389
390static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
391 struct drm_crtc_state *old_crtc_state)
392{
393 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
394 unsigned int pending_planes = 0;
395 int i;
396
397 if (mtk_crtc->event)
398 mtk_crtc->pending_needs_vblank = true;
399 for (i = 0; i < OVL_LAYER_NR; i++) {
400 struct drm_plane *plane = &mtk_crtc->planes[i].base;
401 struct mtk_plane_state *plane_state;
402
403 plane_state = to_mtk_plane_state(plane->state);
404 if (plane_state->pending.dirty) {
405 plane_state->pending.config = true;
406 plane_state->pending.dirty = false;
407 pending_planes |= BIT(i);
408 }
409 }
410 if (pending_planes)
411 mtk_crtc->pending_planes = true;
412}
413
414static const struct drm_crtc_funcs mtk_crtc_funcs = {
415 .set_config = drm_atomic_helper_set_config,
416 .page_flip = drm_atomic_helper_page_flip,
417 .destroy = mtk_drm_crtc_destroy,
418 .reset = mtk_drm_crtc_reset,
419 .atomic_duplicate_state = mtk_drm_crtc_duplicate_state,
420 .atomic_destroy_state = mtk_drm_crtc_destroy_state,
421};
422
423static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
424 .mode_fixup = mtk_drm_crtc_mode_fixup,
425 .mode_set_nofb = mtk_drm_crtc_mode_set_nofb,
426 .enable = mtk_drm_crtc_enable,
427 .disable = mtk_drm_crtc_disable,
428 .atomic_begin = mtk_drm_crtc_atomic_begin,
429 .atomic_flush = mtk_drm_crtc_atomic_flush,
430};
431
432static int mtk_drm_crtc_init(struct drm_device *drm,
433 struct mtk_drm_crtc *mtk_crtc,
434 struct drm_plane *primary,
435 struct drm_plane *cursor, unsigned int pipe)
436{
437 int ret;
438
439 ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
440 &mtk_crtc_funcs, NULL);
441 if (ret)
442 goto err_cleanup_crtc;
443
444 drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
445
446 return 0;
447
448err_cleanup_crtc:
449 drm_crtc_cleanup(&mtk_crtc->base);
450 return ret;
451}
452
453void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
454{
455 struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
456 struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
457 unsigned int i;
458
459 /*
460 * TODO: instead of updating the registers here, we should prepare
461 * working registers in atomic_commit and let the hardware command
462 * queue update module registers on vblank.
463 */
464 if (state->pending_config) {
465 mtk_ddp_comp_config(ovl, state->pending_width,
466 state->pending_height,
467 state->pending_vrefresh);
468
469 state->pending_config = false;
470 }
471
472 if (mtk_crtc->pending_planes) {
473 for (i = 0; i < OVL_LAYER_NR; i++) {
474 struct drm_plane *plane = &mtk_crtc->planes[i].base;
475 struct mtk_plane_state *plane_state;
476
477 plane_state = to_mtk_plane_state(plane->state);
478
479 if (plane_state->pending.config) {
480 mtk_ddp_comp_layer_config(ovl, i, plane_state);
481 plane_state->pending.config = false;
482 }
483 }
484 mtk_crtc->pending_planes = false;
485 }
486
487 mtk_drm_finish_page_flip(mtk_crtc);
488}
489
490int mtk_drm_crtc_create(struct drm_device *drm_dev,
491 const enum mtk_ddp_comp_id *path, unsigned int path_len)
492{
493 struct mtk_drm_private *priv = drm_dev->dev_private;
494 struct device *dev = drm_dev->dev;
495 struct mtk_drm_crtc *mtk_crtc;
496 enum drm_plane_type type;
497 unsigned int zpos;
498 int pipe = priv->num_pipes;
499 int ret;
500 int i;
501
502 for (i = 0; i < path_len; i++) {
503 enum mtk_ddp_comp_id comp_id = path[i];
504 struct device_node *node;
505
506 node = priv->comp_node[comp_id];
507 if (!node) {
508 dev_info(dev,
509 "Not creating crtc %d because component %d is disabled or missing\n",
510 pipe, comp_id);
511 return 0;
512 }
513 }
514
515 mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL);
516 if (!mtk_crtc)
517 return -ENOMEM;
518
519 mtk_crtc->config_regs = priv->config_regs;
520 mtk_crtc->ddp_comp_nr = path_len;
521 mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
522 sizeof(*mtk_crtc->ddp_comp),
523 GFP_KERNEL);
524
525 mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe);
526 if (IS_ERR(mtk_crtc->mutex)) {
527 ret = PTR_ERR(mtk_crtc->mutex);
528 dev_err(dev, "Failed to get mutex: %d\n", ret);
529 return ret;
530 }
531
532 for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
533 enum mtk_ddp_comp_id comp_id = path[i];
534 struct mtk_ddp_comp *comp;
535 struct device_node *node;
536
537 node = priv->comp_node[comp_id];
538 comp = priv->ddp_comp[comp_id];
539 if (!comp) {
540 dev_err(dev, "Component %s not initialized\n",
541 node->full_name);
542 ret = -ENODEV;
543 goto unprepare;
544 }
545
546 ret = clk_prepare(comp->clk);
547 if (ret) {
548 dev_err(dev,
549 "Failed to prepare clock for component %s: %d\n",
550 node->full_name, ret);
551 goto unprepare;
552 }
553
554 mtk_crtc->ddp_comp[i] = comp;
555 }
556
557 for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) {
558 type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY :
559 (zpos == 1) ? DRM_PLANE_TYPE_CURSOR :
560 DRM_PLANE_TYPE_OVERLAY;
561 ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos],
562 BIT(pipe), type, zpos);
563 if (ret)
564 goto unprepare;
565 }
566
567 ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base,
568 &mtk_crtc->planes[1].base, pipe);
569 if (ret < 0)
570 goto unprepare;
571
572 priv->crtc[pipe] = &mtk_crtc->base;
573 priv->num_pipes++;
574
575 return 0;
576
577unprepare:
578 while (--i >= 0)
579 clk_unprepare(mtk_crtc->ddp_comp[i]->clk);
580
581 return ret;
582}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
new file mode 100644
index 000000000000..81e5566ec82f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
@@ -0,0 +1,32 @@
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#ifndef MTK_DRM_CRTC_H
15#define MTK_DRM_CRTC_H
16
17#include <drm/drm_crtc.h>
18#include "mtk_drm_ddp_comp.h"
19#include "mtk_drm_plane.h"
20
21#define OVL_LAYER_NR 4
22
23int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
24void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
25void mtk_drm_crtc_check_flush(struct drm_crtc *crtc);
26void mtk_drm_crtc_commit(struct drm_crtc *crtc);
27void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl);
28int mtk_drm_crtc_create(struct drm_device *drm_dev,
29 const enum mtk_ddp_comp_id *path,
30 unsigned int path_len);
31
32#endif /* MTK_DRM_CRTC_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
new file mode 100644
index 000000000000..17ba9355a49c
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -0,0 +1,353 @@
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 <linux/clk.h>
15#include <linux/module.h>
16#include <linux/of_device.h>
17#include <linux/platform_device.h>
18#include <linux/regmap.h>
19
20#include "mtk_drm_ddp.h"
21#include "mtk_drm_ddp_comp.h"
22
23#define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0x040
24#define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN 0x044
25#define DISP_REG_CONFIG_DISP_OD_MOUT_EN 0x048
26#define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN 0x04c
27#define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN 0x050
28#define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084
29#define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088
30#define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac
31#define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN 0x0c8
32#define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100
33
34#define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * (n))
35#define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n))
36#define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * (n))
37#define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * (n))
38
39#define MUTEX_MOD_DISP_OVL0 BIT(11)
40#define MUTEX_MOD_DISP_OVL1 BIT(12)
41#define MUTEX_MOD_DISP_RDMA0 BIT(13)
42#define MUTEX_MOD_DISP_RDMA1 BIT(14)
43#define MUTEX_MOD_DISP_RDMA2 BIT(15)
44#define MUTEX_MOD_DISP_WDMA0 BIT(16)
45#define MUTEX_MOD_DISP_WDMA1 BIT(17)
46#define MUTEX_MOD_DISP_COLOR0 BIT(18)
47#define MUTEX_MOD_DISP_COLOR1 BIT(19)
48#define MUTEX_MOD_DISP_AAL BIT(20)
49#define MUTEX_MOD_DISP_GAMMA BIT(21)
50#define MUTEX_MOD_DISP_UFOE BIT(22)
51#define MUTEX_MOD_DISP_PWM0 BIT(23)
52#define MUTEX_MOD_DISP_PWM1 BIT(24)
53#define MUTEX_MOD_DISP_OD BIT(25)
54
55#define MUTEX_SOF_SINGLE_MODE 0
56#define MUTEX_SOF_DSI0 1
57#define MUTEX_SOF_DSI1 2
58#define MUTEX_SOF_DPI0 3
59
60#define OVL0_MOUT_EN_COLOR0 0x1
61#define OD_MOUT_EN_RDMA0 0x1
62#define UFOE_MOUT_EN_DSI0 0x1
63#define COLOR0_SEL_IN_OVL0 0x1
64#define OVL1_MOUT_EN_COLOR1 0x1
65#define GAMMA_MOUT_EN_RDMA1 0x1
66#define RDMA1_MOUT_DPI0 0x2
67#define DPI0_SEL_IN_RDMA1 0x1
68#define COLOR1_SEL_IN_OVL1 0x1
69
70struct mtk_disp_mutex {
71 int id;
72 bool claimed;
73};
74
75struct mtk_ddp {
76 struct device *dev;
77 struct clk *clk;
78 void __iomem *regs;
79 struct mtk_disp_mutex mutex[10];
80};
81
82static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
83 [DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL,
84 [DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0,
85 [DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1,
86 [DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA,
87 [DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD,
88 [DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0,
89 [DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1,
90 [DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0,
91 [DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1,
92 [DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0,
93 [DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1,
94 [DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2,
95 [DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE,
96 [DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0,
97 [DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1,
98};
99
100static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
101 enum mtk_ddp_comp_id next,
102 unsigned int *addr)
103{
104 unsigned int value;
105
106 if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
107 *addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
108 value = OVL0_MOUT_EN_COLOR0;
109 } else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
110 *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
111 value = OD_MOUT_EN_RDMA0;
112 } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) {
113 *addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN;
114 value = UFOE_MOUT_EN_DSI0;
115 } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
116 *addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN;
117 value = OVL1_MOUT_EN_COLOR1;
118 } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) {
119 *addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN;
120 value = GAMMA_MOUT_EN_RDMA1;
121 } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
122 *addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN;
123 value = RDMA1_MOUT_DPI0;
124 } else {
125 value = 0;
126 }
127
128 return value;
129}
130
131static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
132 enum mtk_ddp_comp_id next,
133 unsigned int *addr)
134{
135 unsigned int value;
136
137 if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
138 *addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN;
139 value = COLOR0_SEL_IN_OVL0;
140 } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
141 *addr = DISP_REG_CONFIG_DPI_SEL_IN;
142 value = DPI0_SEL_IN_RDMA1;
143 } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
144 *addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
145 value = COLOR1_SEL_IN_OVL1;
146 } else {
147 value = 0;
148 }
149
150 return value;
151}
152
153void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
154 enum mtk_ddp_comp_id cur,
155 enum mtk_ddp_comp_id next)
156{
157 unsigned int addr, value, reg;
158
159 value = mtk_ddp_mout_en(cur, next, &addr);
160 if (value) {
161 reg = readl_relaxed(config_regs + addr) | value;
162 writel_relaxed(reg, config_regs + addr);
163 }
164
165 value = mtk_ddp_sel_in(cur, next, &addr);
166 if (value) {
167 reg = readl_relaxed(config_regs + addr) | value;
168 writel_relaxed(reg, config_regs + addr);
169 }
170}
171
172void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
173 enum mtk_ddp_comp_id cur,
174 enum mtk_ddp_comp_id next)
175{
176 unsigned int addr, value, reg;
177
178 value = mtk_ddp_mout_en(cur, next, &addr);
179 if (value) {
180 reg = readl_relaxed(config_regs + addr) & ~value;
181 writel_relaxed(reg, config_regs + addr);
182 }
183
184 value = mtk_ddp_sel_in(cur, next, &addr);
185 if (value) {
186 reg = readl_relaxed(config_regs + addr) & ~value;
187 writel_relaxed(reg, config_regs + addr);
188 }
189}
190
191struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id)
192{
193 struct mtk_ddp *ddp = dev_get_drvdata(dev);
194
195 if (id >= 10)
196 return ERR_PTR(-EINVAL);
197 if (ddp->mutex[id].claimed)
198 return ERR_PTR(-EBUSY);
199
200 ddp->mutex[id].claimed = true;
201
202 return &ddp->mutex[id];
203}
204
205void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex)
206{
207 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
208 mutex[mutex->id]);
209
210 WARN_ON(&ddp->mutex[mutex->id] != mutex);
211
212 mutex->claimed = false;
213}
214
215int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex)
216{
217 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
218 mutex[mutex->id]);
219 return clk_prepare_enable(ddp->clk);
220}
221
222void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex)
223{
224 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
225 mutex[mutex->id]);
226 clk_disable_unprepare(ddp->clk);
227}
228
229void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
230 enum mtk_ddp_comp_id id)
231{
232 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
233 mutex[mutex->id]);
234 unsigned int reg;
235
236 WARN_ON(&ddp->mutex[mutex->id] != mutex);
237
238 switch (id) {
239 case DDP_COMPONENT_DSI0:
240 reg = MUTEX_SOF_DSI0;
241 break;
242 case DDP_COMPONENT_DSI1:
243 reg = MUTEX_SOF_DSI0;
244 break;
245 case DDP_COMPONENT_DPI0:
246 reg = MUTEX_SOF_DPI0;
247 break;
248 default:
249 reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
250 reg |= mutex_mod[id];
251 writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
252 return;
253 }
254
255 writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
256}
257
258void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
259 enum mtk_ddp_comp_id id)
260{
261 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
262 mutex[mutex->id]);
263 unsigned int reg;
264
265 WARN_ON(&ddp->mutex[mutex->id] != mutex);
266
267 switch (id) {
268 case DDP_COMPONENT_DSI0:
269 case DDP_COMPONENT_DSI1:
270 case DDP_COMPONENT_DPI0:
271 writel_relaxed(MUTEX_SOF_SINGLE_MODE,
272 ddp->regs + DISP_REG_MUTEX_SOF(mutex->id));
273 break;
274 default:
275 reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
276 reg &= ~mutex_mod[id];
277 writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
278 break;
279 }
280}
281
282void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex)
283{
284 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
285 mutex[mutex->id]);
286
287 WARN_ON(&ddp->mutex[mutex->id] != mutex);
288
289 writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
290}
291
292void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex)
293{
294 struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
295 mutex[mutex->id]);
296
297 WARN_ON(&ddp->mutex[mutex->id] != mutex);
298
299 writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
300}
301
302static int mtk_ddp_probe(struct platform_device *pdev)
303{
304 struct device *dev = &pdev->dev;
305 struct mtk_ddp *ddp;
306 struct resource *regs;
307 int i;
308
309 ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL);
310 if (!ddp)
311 return -ENOMEM;
312
313 for (i = 0; i < 10; i++)
314 ddp->mutex[i].id = i;
315
316 ddp->clk = devm_clk_get(dev, NULL);
317 if (IS_ERR(ddp->clk)) {
318 dev_err(dev, "Failed to get clock\n");
319 return PTR_ERR(ddp->clk);
320 }
321
322 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
323 ddp->regs = devm_ioremap_resource(dev, regs);
324 if (IS_ERR(ddp->regs)) {
325 dev_err(dev, "Failed to map mutex registers\n");
326 return PTR_ERR(ddp->regs);
327 }
328
329 platform_set_drvdata(pdev, ddp);
330
331 return 0;
332}
333
334static int mtk_ddp_remove(struct platform_device *pdev)
335{
336 return 0;
337}
338
339static const struct of_device_id ddp_driver_dt_match[] = {
340 { .compatible = "mediatek,mt8173-disp-mutex" },
341 {},
342};
343MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
344
345struct platform_driver mtk_ddp_driver = {
346 .probe = mtk_ddp_probe,
347 .remove = mtk_ddp_remove,
348 .driver = {
349 .name = "mediatek-ddp",
350 .owner = THIS_MODULE,
351 .of_match_table = ddp_driver_dt_match,
352 },
353};
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
new file mode 100644
index 000000000000..92c11752ff65
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
@@ -0,0 +1,41 @@
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#ifndef MTK_DRM_DDP_H
15#define MTK_DRM_DDP_H
16
17#include "mtk_drm_ddp_comp.h"
18
19struct regmap;
20struct device;
21struct mtk_disp_mutex;
22
23void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
24 enum mtk_ddp_comp_id cur,
25 enum mtk_ddp_comp_id next);
26void mtk_ddp_remove_comp_from_path(void __iomem *config_regs,
27 enum mtk_ddp_comp_id cur,
28 enum mtk_ddp_comp_id next);
29
30struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id);
31int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex);
32void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
33 enum mtk_ddp_comp_id id);
34void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex);
35void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex);
36void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
37 enum mtk_ddp_comp_id id);
38void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
39void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
40
41#endif /* MTK_DRM_DDP_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
new file mode 100644
index 000000000000..3970fcf0f05f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -0,0 +1,225 @@
1/*
2 * Copyright (c) 2015 MediaTek Inc.
3 * Authors:
4 * YT Shen <yt.shen@mediatek.com>
5 * CK Hu <ck.hu@mediatek.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/clk.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20#include <linux/of_irq.h>
21#include <linux/of_platform.h>
22#include <linux/platform_device.h>
23#include <drm/drmP.h>
24#include "mtk_drm_drv.h"
25#include "mtk_drm_plane.h"
26#include "mtk_drm_ddp_comp.h"
27
28#define DISP_OD_EN 0x0000
29#define DISP_OD_INTEN 0x0008
30#define DISP_OD_INTSTA 0x000c
31#define DISP_OD_CFG 0x0020
32#define DISP_OD_SIZE 0x0030
33
34#define DISP_REG_UFO_START 0x0000
35
36#define DISP_COLOR_CFG_MAIN 0x0400
37#define DISP_COLOR_START 0x0c00
38#define DISP_COLOR_WIDTH 0x0c50
39#define DISP_COLOR_HEIGHT 0x0c54
40
41#define OD_RELAY_MODE BIT(0)
42
43#define UFO_BYPASS BIT(2)
44
45#define COLOR_BYPASS_ALL BIT(7)
46#define COLOR_SEQ_SEL BIT(13)
47
48static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
49 unsigned int h, unsigned int vrefresh)
50{
51 writel(w, comp->regs + DISP_COLOR_WIDTH);
52 writel(h, comp->regs + DISP_COLOR_HEIGHT);
53}
54
55static void mtk_color_start(struct mtk_ddp_comp *comp)
56{
57 writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
58 comp->regs + DISP_COLOR_CFG_MAIN);
59 writel(0x1, comp->regs + DISP_COLOR_START);
60}
61
62static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
63 unsigned int h, unsigned int vrefresh)
64{
65 writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
66}
67
68static void mtk_od_start(struct mtk_ddp_comp *comp)
69{
70 writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
71 writel(1, comp->regs + DISP_OD_EN);
72}
73
74static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
75{
76 writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
77}
78
79static const struct mtk_ddp_comp_funcs ddp_color = {
80 .config = mtk_color_config,
81 .start = mtk_color_start,
82};
83
84static const struct mtk_ddp_comp_funcs ddp_od = {
85 .config = mtk_od_config,
86 .start = mtk_od_start,
87};
88
89static const struct mtk_ddp_comp_funcs ddp_ufoe = {
90 .start = mtk_ufoe_start,
91};
92
93static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
94 [MTK_DISP_OVL] = "ovl",
95 [MTK_DISP_RDMA] = "rdma",
96 [MTK_DISP_WDMA] = "wdma",
97 [MTK_DISP_COLOR] = "color",
98 [MTK_DISP_AAL] = "aal",
99 [MTK_DISP_GAMMA] = "gamma",
100 [MTK_DISP_UFOE] = "ufoe",
101 [MTK_DSI] = "dsi",
102 [MTK_DPI] = "dpi",
103 [MTK_DISP_PWM] = "pwm",
104 [MTK_DISP_MUTEX] = "mutex",
105 [MTK_DISP_OD] = "od",
106};
107
108struct mtk_ddp_comp_match {
109 enum mtk_ddp_comp_type type;
110 int alias_id;
111 const struct mtk_ddp_comp_funcs *funcs;
112};
113
114static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
115 [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, NULL },
116 [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color },
117 [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color },
118 [DDP_COMPONENT_DPI0] = { MTK_DPI, 0, NULL },
119 [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL },
120 [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL },
121 [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, NULL },
122 [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od },
123 [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL },
124 [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL },
125 [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL },
126 [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, NULL },
127 [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, NULL },
128 [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, NULL },
129 [DDP_COMPONENT_UFOE] = { MTK_DISP_UFOE, 0, &ddp_ufoe },
130 [DDP_COMPONENT_WDMA0] = { MTK_DISP_WDMA, 0, NULL },
131 [DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, NULL },
132};
133
134int mtk_ddp_comp_get_id(struct device_node *node,
135 enum mtk_ddp_comp_type comp_type)
136{
137 int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]);
138 int i;
139
140 for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) {
141 if (comp_type == mtk_ddp_matches[i].type &&
142 (id < 0 || id == mtk_ddp_matches[i].alias_id))
143 return i;
144 }
145
146 return -EINVAL;
147}
148
149int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
150 struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
151 const struct mtk_ddp_comp_funcs *funcs)
152{
153 enum mtk_ddp_comp_type type;
154 struct device_node *larb_node;
155 struct platform_device *larb_pdev;
156
157 if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
158 return -EINVAL;
159
160 comp->id = comp_id;
161 comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
162
163 if (comp_id == DDP_COMPONENT_DPI0 ||
164 comp_id == DDP_COMPONENT_DSI0 ||
165 comp_id == DDP_COMPONENT_PWM0) {
166 comp->regs = NULL;
167 comp->clk = NULL;
168 comp->irq = 0;
169 return 0;
170 }
171
172 comp->regs = of_iomap(node, 0);
173 comp->irq = of_irq_get(node, 0);
174 comp->clk = of_clk_get(node, 0);
175 if (IS_ERR(comp->clk))
176 comp->clk = NULL;
177
178 type = mtk_ddp_matches[comp_id].type;
179
180 /* Only DMA capable components need the LARB property */
181 comp->larb_dev = NULL;
182 if (type != MTK_DISP_OVL &&
183 type != MTK_DISP_RDMA &&
184 type != MTK_DISP_WDMA)
185 return 0;
186
187 larb_node = of_parse_phandle(node, "mediatek,larb", 0);
188 if (!larb_node) {
189 dev_err(dev,
190 "Missing mediadek,larb phandle in %s node\n",
191 node->full_name);
192 return -EINVAL;
193 }
194
195 larb_pdev = of_find_device_by_node(larb_node);
196 if (!larb_pdev) {
197 dev_warn(dev, "Waiting for larb device %s\n",
198 larb_node->full_name);
199 of_node_put(larb_node);
200 return -EPROBE_DEFER;
201 }
202 of_node_put(larb_node);
203
204 comp->larb_dev = &larb_pdev->dev;
205
206 return 0;
207}
208
209int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp)
210{
211 struct mtk_drm_private *private = drm->dev_private;
212
213 if (private->ddp_comp[comp->id])
214 return -EBUSY;
215
216 private->ddp_comp[comp->id] = comp;
217 return 0;
218}
219
220void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp)
221{
222 struct mtk_drm_private *private = drm->dev_private;
223
224 private->ddp_comp[comp->id] = NULL;
225}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
new file mode 100644
index 000000000000..6b13ba97094d
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -0,0 +1,150 @@
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#ifndef MTK_DRM_DDP_COMP_H
15#define MTK_DRM_DDP_COMP_H
16
17#include <linux/io.h>
18
19struct device;
20struct device_node;
21struct drm_crtc;
22struct drm_device;
23struct mtk_plane_state;
24
25enum mtk_ddp_comp_type {
26 MTK_DISP_OVL,
27 MTK_DISP_RDMA,
28 MTK_DISP_WDMA,
29 MTK_DISP_COLOR,
30 MTK_DISP_AAL,
31 MTK_DISP_GAMMA,
32 MTK_DISP_UFOE,
33 MTK_DSI,
34 MTK_DPI,
35 MTK_DISP_PWM,
36 MTK_DISP_MUTEX,
37 MTK_DISP_OD,
38 MTK_DDP_COMP_TYPE_MAX,
39};
40
41enum mtk_ddp_comp_id {
42 DDP_COMPONENT_AAL,
43 DDP_COMPONENT_COLOR0,
44 DDP_COMPONENT_COLOR1,
45 DDP_COMPONENT_DPI0,
46 DDP_COMPONENT_DSI0,
47 DDP_COMPONENT_DSI1,
48 DDP_COMPONENT_GAMMA,
49 DDP_COMPONENT_OD,
50 DDP_COMPONENT_OVL0,
51 DDP_COMPONENT_OVL1,
52 DDP_COMPONENT_PWM0,
53 DDP_COMPONENT_PWM1,
54 DDP_COMPONENT_RDMA0,
55 DDP_COMPONENT_RDMA1,
56 DDP_COMPONENT_RDMA2,
57 DDP_COMPONENT_UFOE,
58 DDP_COMPONENT_WDMA0,
59 DDP_COMPONENT_WDMA1,
60 DDP_COMPONENT_ID_MAX,
61};
62
63struct mtk_ddp_comp;
64
65struct mtk_ddp_comp_funcs {
66 void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
67 unsigned int h, unsigned int vrefresh);
68 void (*start)(struct mtk_ddp_comp *comp);
69 void (*stop)(struct mtk_ddp_comp *comp);
70 void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
71 void (*disable_vblank)(struct mtk_ddp_comp *comp);
72 void (*layer_on)(struct mtk_ddp_comp *comp, unsigned int idx);
73 void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
74 void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
75 struct mtk_plane_state *state);
76};
77
78struct mtk_ddp_comp {
79 struct clk *clk;
80 void __iomem *regs;
81 int irq;
82 struct device *larb_dev;
83 enum mtk_ddp_comp_id id;
84 const struct mtk_ddp_comp_funcs *funcs;
85};
86
87static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
88 unsigned int w, unsigned int h,
89 unsigned int vrefresh)
90{
91 if (comp->funcs && comp->funcs->config)
92 comp->funcs->config(comp, w, h, vrefresh);
93}
94
95static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
96{
97 if (comp->funcs && comp->funcs->start)
98 comp->funcs->start(comp);
99}
100
101static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp)
102{
103 if (comp->funcs && comp->funcs->stop)
104 comp->funcs->stop(comp);
105}
106
107static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp,
108 struct drm_crtc *crtc)
109{
110 if (comp->funcs && comp->funcs->enable_vblank)
111 comp->funcs->enable_vblank(comp, crtc);
112}
113
114static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp)
115{
116 if (comp->funcs && comp->funcs->disable_vblank)
117 comp->funcs->disable_vblank(comp);
118}
119
120static inline void mtk_ddp_comp_layer_on(struct mtk_ddp_comp *comp,
121 unsigned int idx)
122{
123 if (comp->funcs && comp->funcs->layer_on)
124 comp->funcs->layer_on(comp, idx);
125}
126
127static inline void mtk_ddp_comp_layer_off(struct mtk_ddp_comp *comp,
128 unsigned int idx)
129{
130 if (comp->funcs && comp->funcs->layer_off)
131 comp->funcs->layer_off(comp, idx);
132}
133
134static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
135 unsigned int idx,
136 struct mtk_plane_state *state)
137{
138 if (comp->funcs && comp->funcs->layer_config)
139 comp->funcs->layer_config(comp, idx, state);
140}
141
142int mtk_ddp_comp_get_id(struct device_node *node,
143 enum mtk_ddp_comp_type comp_type);
144int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
145 struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
146 const struct mtk_ddp_comp_funcs *funcs);
147int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp);
148void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp);
149
150#endif /* MTK_DRM_DDP_COMP_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
new file mode 100644
index 000000000000..ade6720149b1
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -0,0 +1,564 @@
1/*
2 * Copyright (c) 2015 MediaTek Inc.
3 * Author: YT SHEN <yt.shen@mediatek.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <drm/drmP.h>
16#include <drm/drm_atomic.h>
17#include <drm/drm_atomic_helper.h>
18#include <drm/drm_crtc_helper.h>
19#include <drm/drm_gem.h>
20#include <drm/drm_gem_cma_helper.h>
21#include <linux/component.h>
22#include <linux/iommu.h>
23#include <linux/of_address.h>
24#include <linux/of_platform.h>
25#include <linux/pm_runtime.h>
26
27#include "mtk_drm_crtc.h"
28#include "mtk_drm_ddp.h"
29#include "mtk_drm_ddp_comp.h"
30#include "mtk_drm_drv.h"
31#include "mtk_drm_fb.h"
32#include "mtk_drm_gem.h"
33
34#define DRIVER_NAME "mediatek"
35#define DRIVER_DESC "Mediatek SoC DRM"
36#define DRIVER_DATE "20150513"
37#define DRIVER_MAJOR 1
38#define DRIVER_MINOR 0
39
40static void mtk_atomic_schedule(struct mtk_drm_private *private,
41 struct drm_atomic_state *state)
42{
43 private->commit.state = state;
44 schedule_work(&private->commit.work);
45}
46
47static void mtk_atomic_wait_for_fences(struct drm_atomic_state *state)
48{
49 struct drm_plane *plane;
50 struct drm_plane_state *plane_state;
51 int i;
52
53 for_each_plane_in_state(state, plane, plane_state, i)
54 mtk_fb_wait(plane->state->fb);
55}
56
57static void mtk_atomic_complete(struct mtk_drm_private *private,
58 struct drm_atomic_state *state)
59{
60 struct drm_device *drm = private->drm;
61
62 mtk_atomic_wait_for_fences(state);
63
64 drm_atomic_helper_commit_modeset_disables(drm, state);
65 drm_atomic_helper_commit_planes(drm, state, false);
66 drm_atomic_helper_commit_modeset_enables(drm, state);
67 drm_atomic_helper_wait_for_vblanks(drm, state);
68 drm_atomic_helper_cleanup_planes(drm, state);
69 drm_atomic_state_free(state);
70}
71
72static void mtk_atomic_work(struct work_struct *work)
73{
74 struct mtk_drm_private *private = container_of(work,
75 struct mtk_drm_private, commit.work);
76
77 mtk_atomic_complete(private, private->commit.state);
78}
79
80static int mtk_atomic_commit(struct drm_device *drm,
81 struct drm_atomic_state *state,
82 bool async)
83{
84 struct mtk_drm_private *private = drm->dev_private;
85 int ret;
86
87 ret = drm_atomic_helper_prepare_planes(drm, state);
88 if (ret)
89 return ret;
90
91 mutex_lock(&private->commit.lock);
92 flush_work(&private->commit.work);
93
94 drm_atomic_helper_swap_state(drm, state);
95
96 if (async)
97 mtk_atomic_schedule(private, state);
98 else
99 mtk_atomic_complete(private, state);
100
101 mutex_unlock(&private->commit.lock);
102
103 return 0;
104}
105
106static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
107 .fb_create = mtk_drm_mode_fb_create,
108 .atomic_check = drm_atomic_helper_check,
109 .atomic_commit = mtk_atomic_commit,
110};
111
112static const enum mtk_ddp_comp_id mtk_ddp_main[] = {
113 DDP_COMPONENT_OVL0,
114 DDP_COMPONENT_COLOR0,
115 DDP_COMPONENT_AAL,
116 DDP_COMPONENT_OD,
117 DDP_COMPONENT_RDMA0,
118 DDP_COMPONENT_UFOE,
119 DDP_COMPONENT_DSI0,
120 DDP_COMPONENT_PWM0,
121};
122
123static const enum mtk_ddp_comp_id mtk_ddp_ext[] = {
124 DDP_COMPONENT_OVL1,
125 DDP_COMPONENT_COLOR1,
126 DDP_COMPONENT_GAMMA,
127 DDP_COMPONENT_RDMA1,
128 DDP_COMPONENT_DPI0,
129};
130
131static int mtk_drm_kms_init(struct drm_device *drm)
132{
133 struct mtk_drm_private *private = drm->dev_private;
134 struct platform_device *pdev;
135 struct device_node *np;
136 int ret;
137
138 if (!iommu_present(&platform_bus_type))
139 return -EPROBE_DEFER;
140
141 pdev = of_find_device_by_node(private->mutex_node);
142 if (!pdev) {
143 dev_err(drm->dev, "Waiting for disp-mutex device %s\n",
144 private->mutex_node->full_name);
145 of_node_put(private->mutex_node);
146 return -EPROBE_DEFER;
147 }
148 private->mutex_dev = &pdev->dev;
149
150 drm_mode_config_init(drm);
151
152 drm->mode_config.min_width = 64;
153 drm->mode_config.min_height = 64;
154
155 /*
156 * set max width and height as default value(4096x4096).
157 * this value would be used to check framebuffer size limitation
158 * at drm_mode_addfb().
159 */
160 drm->mode_config.max_width = 4096;
161 drm->mode_config.max_height = 4096;
162 drm->mode_config.funcs = &mtk_drm_mode_config_funcs;
163
164 ret = component_bind_all(drm->dev, drm);
165 if (ret)
166 goto err_config_cleanup;
167
168 /*
169 * We currently support two fixed data streams, each optional,
170 * and each statically assigned to a crtc:
171 * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ...
172 */
173 ret = mtk_drm_crtc_create(drm, mtk_ddp_main, ARRAY_SIZE(mtk_ddp_main));
174 if (ret < 0)
175 goto err_component_unbind;
176 /* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */
177 ret = mtk_drm_crtc_create(drm, mtk_ddp_ext, ARRAY_SIZE(mtk_ddp_ext));
178 if (ret < 0)
179 goto err_component_unbind;
180
181 /* Use OVL device for all DMA memory allocations */
182 np = private->comp_node[mtk_ddp_main[0]] ?:
183 private->comp_node[mtk_ddp_ext[0]];
184 pdev = of_find_device_by_node(np);
185 if (!pdev) {
186 ret = -ENODEV;
187 dev_err(drm->dev, "Need at least one OVL device\n");
188 goto err_component_unbind;
189 }
190
191 private->dma_dev = &pdev->dev;
192
193 /*
194 * We don't use the drm_irq_install() helpers provided by the DRM
195 * core, so we need to set this manually in order to allow the
196 * DRM_IOCTL_WAIT_VBLANK to operate correctly.
197 */
198 drm->irq_enabled = true;
199 ret = drm_vblank_init(drm, MAX_CRTC);
200 if (ret < 0)
201 goto err_component_unbind;
202
203 drm_kms_helper_poll_init(drm);
204 drm_mode_config_reset(drm);
205
206 return 0;
207
208err_component_unbind:
209 component_unbind_all(drm->dev, drm);
210err_config_cleanup:
211 drm_mode_config_cleanup(drm);
212
213 return ret;
214}
215
216static void mtk_drm_kms_deinit(struct drm_device *drm)
217{
218 drm_kms_helper_poll_fini(drm);
219
220 drm_vblank_cleanup(drm);
221 component_unbind_all(drm->dev, drm);
222 drm_mode_config_cleanup(drm);
223}
224
225static const struct file_operations mtk_drm_fops = {
226 .owner = THIS_MODULE,
227 .open = drm_open,
228 .release = drm_release,
229 .unlocked_ioctl = drm_ioctl,
230 .mmap = mtk_drm_gem_mmap,
231 .poll = drm_poll,
232 .read = drm_read,
233#ifdef CONFIG_COMPAT
234 .compat_ioctl = drm_compat_ioctl,
235#endif
236};
237
238static struct drm_driver mtk_drm_driver = {
239 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
240 DRIVER_ATOMIC,
241
242 .get_vblank_counter = drm_vblank_count,
243 .enable_vblank = mtk_drm_crtc_enable_vblank,
244 .disable_vblank = mtk_drm_crtc_disable_vblank,
245
246 .gem_free_object = mtk_drm_gem_free_object,
247 .gem_vm_ops = &drm_gem_cma_vm_ops,
248 .dumb_create = mtk_drm_gem_dumb_create,
249 .dumb_map_offset = mtk_drm_gem_dumb_map_offset,
250 .dumb_destroy = drm_gem_dumb_destroy,
251
252 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
253 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
254 .gem_prime_export = drm_gem_prime_export,
255 .gem_prime_import = drm_gem_prime_import,
256 .gem_prime_get_sg_table = mtk_gem_prime_get_sg_table,
257 .gem_prime_import_sg_table = mtk_gem_prime_import_sg_table,
258 .gem_prime_mmap = mtk_drm_gem_mmap_buf,
259 .fops = &mtk_drm_fops,
260
261 .name = DRIVER_NAME,
262 .desc = DRIVER_DESC,
263 .date = DRIVER_DATE,
264 .major = DRIVER_MAJOR,
265 .minor = DRIVER_MINOR,
266};
267
268static int compare_of(struct device *dev, void *data)
269{
270 return dev->of_node == data;
271}
272
273static int mtk_drm_bind(struct device *dev)
274{
275 struct mtk_drm_private *private = dev_get_drvdata(dev);
276 struct drm_device *drm;
277 int ret;
278
279 drm = drm_dev_alloc(&mtk_drm_driver, dev);
280 if (!drm)
281 return -ENOMEM;
282
283 drm_dev_set_unique(drm, dev_name(dev));
284
285 drm->dev_private = private;
286 private->drm = drm;
287
288 ret = mtk_drm_kms_init(drm);
289 if (ret < 0)
290 goto err_free;
291
292 ret = drm_dev_register(drm, 0);
293 if (ret < 0)
294 goto err_deinit;
295
296 ret = drm_connector_register_all(drm);
297 if (ret < 0)
298 goto err_unregister;
299
300 return 0;
301
302err_unregister:
303 drm_dev_unregister(drm);
304err_deinit:
305 mtk_drm_kms_deinit(drm);
306err_free:
307 drm_dev_unref(drm);
308 return ret;
309}
310
311static void mtk_drm_unbind(struct device *dev)
312{
313 struct mtk_drm_private *private = dev_get_drvdata(dev);
314
315 drm_put_dev(private->drm);
316 private->drm = NULL;
317}
318
319static const struct component_master_ops mtk_drm_ops = {
320 .bind = mtk_drm_bind,
321 .unbind = mtk_drm_unbind,
322};
323
324static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
325 { .compatible = "mediatek,mt8173-disp-ovl", .data = (void *)MTK_DISP_OVL },
326 { .compatible = "mediatek,mt8173-disp-rdma", .data = (void *)MTK_DISP_RDMA },
327 { .compatible = "mediatek,mt8173-disp-wdma", .data = (void *)MTK_DISP_WDMA },
328 { .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
329 { .compatible = "mediatek,mt8173-disp-aal", .data = (void *)MTK_DISP_AAL},
330 { .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
331 { .compatible = "mediatek,mt8173-disp-ufoe", .data = (void *)MTK_DISP_UFOE },
332 { .compatible = "mediatek,mt8173-dsi", .data = (void *)MTK_DSI },
333 { .compatible = "mediatek,mt8173-dpi", .data = (void *)MTK_DPI },
334 { .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
335 { .compatible = "mediatek,mt8173-disp-pwm", .data = (void *)MTK_DISP_PWM },
336 { .compatible = "mediatek,mt8173-disp-od", .data = (void *)MTK_DISP_OD },
337 { }
338};
339
340static int mtk_drm_probe(struct platform_device *pdev)
341{
342 struct device *dev = &pdev->dev;
343 struct mtk_drm_private *private;
344 struct resource *mem;
345 struct device_node *node;
346 struct component_match *match = NULL;
347 int ret;
348 int i;
349
350 private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL);
351 if (!private)
352 return -ENOMEM;
353
354 mutex_init(&private->commit.lock);
355 INIT_WORK(&private->commit.work, mtk_atomic_work);
356
357 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
358 private->config_regs = devm_ioremap_resource(dev, mem);
359 if (IS_ERR(private->config_regs)) {
360 ret = PTR_ERR(private->config_regs);
361 dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n",
362 ret);
363 return ret;
364 }
365
366 /* Iterate over sibling DISP function blocks */
367 for_each_child_of_node(dev->of_node->parent, node) {
368 const struct of_device_id *of_id;
369 enum mtk_ddp_comp_type comp_type;
370 int comp_id;
371
372 of_id = of_match_node(mtk_ddp_comp_dt_ids, node);
373 if (!of_id)
374 continue;
375
376 if (!of_device_is_available(node)) {
377 dev_dbg(dev, "Skipping disabled component %s\n",
378 node->full_name);
379 continue;
380 }
381
382 comp_type = (enum mtk_ddp_comp_type)of_id->data;
383
384 if (comp_type == MTK_DISP_MUTEX) {
385 private->mutex_node = of_node_get(node);
386 continue;
387 }
388
389 comp_id = mtk_ddp_comp_get_id(node, comp_type);
390 if (comp_id < 0) {
391 dev_warn(dev, "Skipping unknown component %s\n",
392 node->full_name);
393 continue;
394 }
395
396 private->comp_node[comp_id] = of_node_get(node);
397
398 /*
399 * Currently only the OVL, RDMA, DSI, and DPI blocks have
400 * separate component platform drivers and initialize their own
401 * DDP component structure. The others are initialized here.
402 */
403 if (comp_type == MTK_DISP_OVL ||
404 comp_type == MTK_DISP_RDMA ||
405 comp_type == MTK_DSI ||
406 comp_type == MTK_DPI) {
407 dev_info(dev, "Adding component match for %s\n",
408 node->full_name);
409 component_match_add(dev, &match, compare_of, node);
410 } else {
411 struct mtk_ddp_comp *comp;
412
413 comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
414 if (!comp) {
415 ret = -ENOMEM;
416 goto err_node;
417 }
418
419 ret = mtk_ddp_comp_init(dev, node, comp, comp_id, NULL);
420 if (ret)
421 goto err_node;
422
423 private->ddp_comp[comp_id] = comp;
424 }
425 }
426
427 if (!private->mutex_node) {
428 dev_err(dev, "Failed to find disp-mutex node\n");
429 ret = -ENODEV;
430 goto err_node;
431 }
432
433 pm_runtime_enable(dev);
434
435 platform_set_drvdata(pdev, private);
436
437 ret = component_master_add_with_match(dev, &mtk_drm_ops, match);
438 if (ret)
439 goto err_pm;
440
441 return 0;
442
443err_pm:
444 pm_runtime_disable(dev);
445err_node:
446 of_node_put(private->mutex_node);
447 for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
448 of_node_put(private->comp_node[i]);
449 return ret;
450}
451
452static int mtk_drm_remove(struct platform_device *pdev)
453{
454 struct mtk_drm_private *private = platform_get_drvdata(pdev);
455 struct drm_device *drm = private->drm;
456 int i;
457
458 drm_connector_unregister_all(drm);
459 drm_dev_unregister(drm);
460 mtk_drm_kms_deinit(drm);
461 drm_dev_unref(drm);
462
463 component_master_del(&pdev->dev, &mtk_drm_ops);
464 pm_runtime_disable(&pdev->dev);
465 of_node_put(private->mutex_node);
466 for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
467 of_node_put(private->comp_node[i]);
468
469 return 0;
470}
471
472#ifdef CONFIG_PM_SLEEP
473static int mtk_drm_sys_suspend(struct device *dev)
474{
475 struct mtk_drm_private *private = dev_get_drvdata(dev);
476 struct drm_device *drm = private->drm;
477
478 drm_kms_helper_poll_disable(drm);
479
480 private->suspend_state = drm_atomic_helper_suspend(drm);
481 if (IS_ERR(private->suspend_state)) {
482 drm_kms_helper_poll_enable(drm);
483 return PTR_ERR(private->suspend_state);
484 }
485
486 DRM_DEBUG_DRIVER("mtk_drm_sys_suspend\n");
487 return 0;
488}
489
490static int mtk_drm_sys_resume(struct device *dev)
491{
492 struct mtk_drm_private *private = dev_get_drvdata(dev);
493 struct drm_device *drm = private->drm;
494
495 drm_atomic_helper_resume(drm, private->suspend_state);
496 drm_kms_helper_poll_enable(drm);
497
498 DRM_DEBUG_DRIVER("mtk_drm_sys_resume\n");
499 return 0;
500}
501#endif
502
503static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
504 mtk_drm_sys_resume);
505
506static const struct of_device_id mtk_drm_of_ids[] = {
507 { .compatible = "mediatek,mt8173-mmsys", },
508 { }
509};
510
511static struct platform_driver mtk_drm_platform_driver = {
512 .probe = mtk_drm_probe,
513 .remove = mtk_drm_remove,
514 .driver = {
515 .name = "mediatek-drm",
516 .of_match_table = mtk_drm_of_ids,
517 .pm = &mtk_drm_pm_ops,
518 },
519};
520
521static struct platform_driver * const mtk_drm_drivers[] = {
522 &mtk_ddp_driver,
523 &mtk_disp_ovl_driver,
524 &mtk_disp_rdma_driver,
525 &mtk_drm_platform_driver,
526};
527
528static int __init mtk_drm_init(void)
529{
530 int ret;
531 int i;
532
533 for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) {
534 ret = platform_driver_register(mtk_drm_drivers[i]);
535 if (ret < 0) {
536 pr_err("Failed to register %s driver: %d\n",
537 mtk_drm_drivers[i]->driver.name, ret);
538 goto err;
539 }
540 }
541
542 return 0;
543
544err:
545 while (--i >= 0)
546 platform_driver_unregister(mtk_drm_drivers[i]);
547
548 return ret;
549}
550
551static void __exit mtk_drm_exit(void)
552{
553 int i;
554
555 for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--)
556 platform_driver_unregister(mtk_drm_drivers[i]);
557}
558
559module_init(mtk_drm_init);
560module_exit(mtk_drm_exit);
561
562MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>");
563MODULE_DESCRIPTION("Mediatek SoC DRM driver");
564MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
new file mode 100644
index 000000000000..27dc8fadd6fd
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -0,0 +1,57 @@
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#ifndef MTK_DRM_DRV_H
15#define MTK_DRM_DRV_H
16
17#include <linux/io.h>
18#include "mtk_drm_ddp_comp.h"
19
20#define MAX_CRTC 2
21#define MAX_CONNECTOR 2
22
23struct device;
24struct device_node;
25struct drm_crtc;
26struct drm_device;
27struct drm_fb_helper;
28struct drm_property;
29struct regmap;
30
31struct mtk_drm_private {
32 struct drm_device *drm;
33 struct device *dma_dev;
34
35 struct drm_crtc *crtc[MAX_CRTC];
36 unsigned int num_pipes;
37
38 struct device_node *mutex_node;
39 struct device *mutex_dev;
40 void __iomem *config_regs;
41 struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
42 struct mtk_ddp_comp *ddp_comp[DDP_COMPONENT_ID_MAX];
43
44 struct {
45 struct drm_atomic_state *state;
46 struct work_struct work;
47 struct mutex lock;
48 } commit;
49
50 struct drm_atomic_state *suspend_state;
51};
52
53extern struct platform_driver mtk_ddp_driver;
54extern struct platform_driver mtk_disp_ovl_driver;
55extern struct platform_driver mtk_disp_rdma_driver;
56
57#endif /* MTK_DRM_DRV_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
new file mode 100644
index 000000000000..33d30c19f35f
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c
@@ -0,0 +1,165 @@
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 <drm/drm_crtc_helper.h>
16#include <drm/drm_fb_helper.h>
17#include <drm/drm_gem.h>
18#include <linux/dma-buf.h>
19#include <linux/reservation.h>
20
21#include "mtk_drm_drv.h"
22#include "mtk_drm_fb.h"
23#include "mtk_drm_gem.h"
24
25/*
26 * mtk specific framebuffer structure.
27 *
28 * @fb: drm framebuffer object.
29 * @gem_obj: array of gem objects.
30 */
31struct mtk_drm_fb {
32 struct drm_framebuffer base;
33 /* For now we only support a single plane */
34 struct drm_gem_object *gem_obj;
35};
36
37#define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base)
38
39struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb)
40{
41 struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
42
43 return mtk_fb->gem_obj;
44}
45
46static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb,
47 struct drm_file *file_priv,
48 unsigned int *handle)
49{
50 struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
51
52 return drm_gem_handle_create(file_priv, mtk_fb->gem_obj, handle);
53}
54
55static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
56{
57 struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
58
59 drm_framebuffer_cleanup(fb);
60
61 drm_gem_object_unreference_unlocked(mtk_fb->gem_obj);
62
63 kfree(mtk_fb);
64}
65
66static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = {
67 .create_handle = mtk_drm_fb_create_handle,
68 .destroy = mtk_drm_fb_destroy,
69};
70
71static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev,
72 const struct drm_mode_fb_cmd2 *mode,
73 struct drm_gem_object *obj)
74{
75 struct mtk_drm_fb *mtk_fb;
76 int ret;
77
78 if (drm_format_num_planes(mode->pixel_format) != 1)
79 return ERR_PTR(-EINVAL);
80
81 mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL);
82 if (!mtk_fb)
83 return ERR_PTR(-ENOMEM);
84
85 drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode);
86
87 mtk_fb->gem_obj = obj;
88
89 ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs);
90 if (ret) {
91 DRM_ERROR("failed to initialize framebuffer\n");
92 kfree(mtk_fb);
93 return ERR_PTR(ret);
94 }
95
96 return mtk_fb;
97}
98
99/*
100 * Wait for any exclusive fence in fb's gem object's reservation object.
101 *
102 * Returns -ERESTARTSYS if interrupted, else 0.
103 */
104int mtk_fb_wait(struct drm_framebuffer *fb)
105{
106 struct drm_gem_object *gem;
107 struct reservation_object *resv;
108 long ret;
109
110 if (!fb)
111 return 0;
112
113 gem = mtk_fb_get_gem_obj(fb);
114 if (!gem || !gem->dma_buf || !gem->dma_buf->resv)
115 return 0;
116
117 resv = gem->dma_buf->resv;
118 ret = reservation_object_wait_timeout_rcu(resv, false, true,
119 MAX_SCHEDULE_TIMEOUT);
120 /* MAX_SCHEDULE_TIMEOUT on success, -ERESTARTSYS if interrupted */
121 if (WARN_ON(ret < 0))
122 return ret;
123
124 return 0;
125}
126
127struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
128 struct drm_file *file,
129 const struct drm_mode_fb_cmd2 *cmd)
130{
131 struct mtk_drm_fb *mtk_fb;
132 struct drm_gem_object *gem;
133 unsigned int width = cmd->width;
134 unsigned int height = cmd->height;
135 unsigned int size, bpp;
136 int ret;
137
138 if (drm_format_num_planes(cmd->pixel_format) != 1)
139 return ERR_PTR(-EINVAL);
140
141 gem = drm_gem_object_lookup(dev, file, cmd->handles[0]);
142 if (!gem)
143 return ERR_PTR(-ENOENT);
144
145 bpp = drm_format_plane_cpp(cmd->pixel_format, 0);
146 size = (height - 1) * cmd->pitches[0] + width * bpp;
147 size += cmd->offsets[0];
148
149 if (gem->size < size) {
150 ret = -EINVAL;
151 goto unreference;
152 }
153
154 mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
155 if (IS_ERR(mtk_fb)) {
156 ret = PTR_ERR(mtk_fb);
157 goto unreference;
158 }
159
160 return &mtk_fb->base;
161
162unreference:
163 drm_gem_object_unreference_unlocked(gem);
164 return ERR_PTR(ret);
165}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
new file mode 100644
index 000000000000..9b2ae345a4e9
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h
@@ -0,0 +1,23 @@
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#ifndef MTK_DRM_FB_H
15#define MTK_DRM_FB_H
16
17struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb);
18int mtk_fb_wait(struct drm_framebuffer *fb);
19struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
20 struct drm_file *file,
21 const struct drm_mode_fb_cmd2 *cmd);
22
23#endif /* MTK_DRM_FB_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
new file mode 100644
index 000000000000..a773bfaea913
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -0,0 +1,269 @@
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 <drm/drm_gem.h>
16#include <linux/dma-buf.h>
17
18#include "mtk_drm_drv.h"
19#include "mtk_drm_gem.h"
20
21static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev,
22 unsigned long size)
23{
24 struct mtk_drm_gem_obj *mtk_gem_obj;
25 int ret;
26
27 size = round_up(size, PAGE_SIZE);
28
29 mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL);
30 if (!mtk_gem_obj)
31 return ERR_PTR(-ENOMEM);
32
33 ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size);
34 if (ret < 0) {
35 DRM_ERROR("failed to initialize gem object\n");
36 kfree(mtk_gem_obj);
37 return ERR_PTR(ret);
38 }
39
40 return mtk_gem_obj;
41}
42
43struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
44 size_t size, bool alloc_kmap)
45{
46 struct mtk_drm_private *priv = dev->dev_private;
47 struct mtk_drm_gem_obj *mtk_gem;
48 struct drm_gem_object *obj;
49 int ret;
50
51 mtk_gem = mtk_drm_gem_init(dev, size);
52 if (IS_ERR(mtk_gem))
53 return ERR_CAST(mtk_gem);
54
55 obj = &mtk_gem->base;
56
57 init_dma_attrs(&mtk_gem->dma_attrs);
58 dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
59
60 if (!alloc_kmap)
61 dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
62
63 mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
64 &mtk_gem->dma_addr, GFP_KERNEL,
65 &mtk_gem->dma_attrs);
66 if (!mtk_gem->cookie) {
67 DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
68 ret = -ENOMEM;
69 goto err_gem_free;
70 }
71
72 if (alloc_kmap)
73 mtk_gem->kvaddr = mtk_gem->cookie;
74
75 DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n",
76 mtk_gem->cookie, &mtk_gem->dma_addr,
77 size);
78
79 return mtk_gem;
80
81err_gem_free:
82 drm_gem_object_release(obj);
83 kfree(mtk_gem);
84 return ERR_PTR(ret);
85}
86
87void mtk_drm_gem_free_object(struct drm_gem_object *obj)
88{
89 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
90 struct mtk_drm_private *priv = obj->dev->dev_private;
91
92 if (mtk_gem->sg)
93 drm_prime_gem_destroy(obj, mtk_gem->sg);
94 else
95 dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
96 mtk_gem->dma_addr, &mtk_gem->dma_attrs);
97
98 /* release file pointer to gem object. */
99 drm_gem_object_release(obj);
100
101 kfree(mtk_gem);
102}
103
104int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
105 struct drm_mode_create_dumb *args)
106{
107 struct mtk_drm_gem_obj *mtk_gem;
108 int ret;
109
110 args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
111 args->size = args->pitch * args->height;
112
113 mtk_gem = mtk_drm_gem_create(dev, args->size, false);
114 if (IS_ERR(mtk_gem))
115 return PTR_ERR(mtk_gem);
116
117 /*
118 * allocate a id of idr table where the obj is registered
119 * and handle has the id what user can see.
120 */
121 ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle);
122 if (ret)
123 goto err_handle_create;
124
125 /* drop reference from allocate - handle holds it now. */
126 drm_gem_object_unreference_unlocked(&mtk_gem->base);
127
128 return 0;
129
130err_handle_create:
131 mtk_drm_gem_free_object(&mtk_gem->base);
132 return ret;
133}
134
135int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
136 struct drm_device *dev, uint32_t handle,
137 uint64_t *offset)
138{
139 struct drm_gem_object *obj;
140 int ret;
141
142 obj = drm_gem_object_lookup(dev, file_priv, handle);
143 if (!obj) {
144 DRM_ERROR("failed to lookup gem object.\n");
145 return -EINVAL;
146 }
147
148 ret = drm_gem_create_mmap_offset(obj);
149 if (ret)
150 goto out;
151
152 *offset = drm_vma_node_offset_addr(&obj->vma_node);
153 DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);
154
155out:
156 drm_gem_object_unreference_unlocked(obj);
157 return ret;
158}
159
160static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
161 struct vm_area_struct *vma)
162
163{
164 int ret;
165 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
166 struct mtk_drm_private *priv = obj->dev->dev_private;
167
168 /*
169 * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear
170 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
171 */
172 vma->vm_flags &= ~VM_PFNMAP;
173 vma->vm_pgoff = 0;
174
175 ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
176 mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
177 if (ret)
178 drm_gem_vm_close(vma);
179
180 return ret;
181}
182
183int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma)
184{
185 int ret;
186
187 ret = drm_gem_mmap_obj(obj, obj->size, vma);
188 if (ret)
189 return ret;
190
191 return mtk_drm_gem_object_mmap(obj, vma);
192}
193
194int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
195{
196 struct drm_gem_object *obj;
197 int ret;
198
199 ret = drm_gem_mmap(filp, vma);
200 if (ret)
201 return ret;
202
203 obj = vma->vm_private_data;
204
205 return mtk_drm_gem_object_mmap(obj, vma);
206}
207
208/*
209 * Allocate a sg_table for this GEM object.
210 * Note: Both the table's contents, and the sg_table itself must be freed by
211 * the caller.
212 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
213 */
214struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
215{
216 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj);
217 struct mtk_drm_private *priv = obj->dev->dev_private;
218 struct sg_table *sgt;
219 int ret;
220
221 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
222 if (!sgt)
223 return ERR_PTR(-ENOMEM);
224
225 ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
226 mtk_gem->dma_addr, obj->size,
227 &mtk_gem->dma_attrs);
228 if (ret) {
229 DRM_ERROR("failed to allocate sgt, %d\n", ret);
230 kfree(sgt);
231 return ERR_PTR(ret);
232 }
233
234 return sgt;
235}
236
237struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
238 struct dma_buf_attachment *attach, struct sg_table *sg)
239{
240 struct mtk_drm_gem_obj *mtk_gem;
241 int ret;
242 struct scatterlist *s;
243 unsigned int i;
244 dma_addr_t expected;
245
246 mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size);
247
248 if (IS_ERR(mtk_gem))
249 return ERR_PTR(PTR_ERR(mtk_gem));
250
251 expected = sg_dma_address(sg->sgl);
252 for_each_sg(sg->sgl, s, sg->nents, i) {
253 if (sg_dma_address(s) != expected) {
254 DRM_ERROR("sg_table is not contiguous");
255 ret = -EINVAL;
256 goto err_gem_free;
257 }
258 expected = sg_dma_address(s) + sg_dma_len(s);
259 }
260
261 mtk_gem->dma_addr = sg_dma_address(sg->sgl);
262 mtk_gem->sg = sg;
263
264 return &mtk_gem->base;
265
266err_gem_free:
267 kfree(mtk_gem);
268 return ERR_PTR(ret);
269}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
new file mode 100644
index 000000000000..3a2a5624a1cb
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
@@ -0,0 +1,59 @@
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#ifndef _MTK_DRM_GEM_H_
15#define _MTK_DRM_GEM_H_
16
17#include <drm/drm_gem.h>
18
19/*
20 * mtk drm buffer structure.
21 *
22 * @base: a gem object.
23 * - a new handle to this gem object would be created
24 * by drm_gem_handle_create().
25 * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs()
26 * @kvaddr: kernel virtual address of gem buffer.
27 * @dma_addr: dma address of gem buffer.
28 * @dma_attrs: dma attributes of gem buffer.
29 *
30 * P.S. this object would be transferred to user as kms_bo.handle so
31 * user can access the buffer through kms_bo.handle.
32 */
33struct mtk_drm_gem_obj {
34 struct drm_gem_object base;
35 void *cookie;
36 void *kvaddr;
37 dma_addr_t dma_addr;
38 struct dma_attrs dma_attrs;
39 struct sg_table *sg;
40};
41
42#define to_mtk_gem_obj(x) container_of(x, struct mtk_drm_gem_obj, base)
43
44void mtk_drm_gem_free_object(struct drm_gem_object *gem);
45struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, size_t size,
46 bool alloc_kmap);
47int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
48 struct drm_mode_create_dumb *args);
49int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
50 struct drm_device *dev, uint32_t handle,
51 uint64_t *offset);
52int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
53int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj,
54 struct vm_area_struct *vma);
55struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj);
56struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev,
57 struct dma_buf_attachment *attach, struct sg_table *sg);
58
59#endif
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
new file mode 100644
index 000000000000..c898788f3dd3
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
@@ -0,0 +1,240 @@
1/*
2 * Copyright (c) 2015 MediaTek Inc.
3 * Author: CK Hu <ck.hu@mediatek.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <drm/drmP.h>
16#include <drm/drm_atomic.h>
17#include <drm/drm_atomic_helper.h>
18#include <drm/drm_plane_helper.h>
19
20#include "mtk_drm_crtc.h"
21#include "mtk_drm_ddp_comp.h"
22#include "mtk_drm_drv.h"
23#include "mtk_drm_fb.h"
24#include "mtk_drm_gem.h"
25#include "mtk_drm_plane.h"
26
27static const u32 formats[] = {
28 DRM_FORMAT_XRGB8888,
29 DRM_FORMAT_ARGB8888,
30 DRM_FORMAT_RGB565,
31};
32
33static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable,
34 dma_addr_t addr, struct drm_rect *dest)
35{
36 struct drm_plane *plane = &mtk_plane->base;
37 struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
38 unsigned int pitch, format;
39 int x, y;
40
41 if (WARN_ON(!plane->state || (enable && !plane->state->fb)))
42 return;
43
44 if (plane->state->fb) {
45 pitch = plane->state->fb->pitches[0];
46 format = plane->state->fb->pixel_format;
47 } else {
48 pitch = 0;
49 format = DRM_FORMAT_RGBA8888;
50 }
51
52 x = plane->state->crtc_x;
53 y = plane->state->crtc_y;
54
55 if (x < 0) {
56 addr -= x * 4;
57 x = 0;
58 }
59
60 if (y < 0) {
61 addr -= y * pitch;
62 y = 0;
63 }
64
65 state->pending.enable = enable;
66 state->pending.pitch = pitch;
67 state->pending.format = format;
68 state->pending.addr = addr;
69 state->pending.x = x;
70 state->pending.y = y;
71 state->pending.width = dest->x2 - dest->x1;
72 state->pending.height = dest->y2 - dest->y1;
73 wmb(); /* Make sure the above parameters are set before update */
74 state->pending.dirty = true;
75}
76
77static void mtk_plane_reset(struct drm_plane *plane)
78{
79 struct mtk_plane_state *state;
80
81 if (plane->state) {
82 if (plane->state->fb)
83 drm_framebuffer_unreference(plane->state->fb);
84
85 state = to_mtk_plane_state(plane->state);
86 memset(state, 0, sizeof(*state));
87 } else {
88 state = kzalloc(sizeof(*state), GFP_KERNEL);
89 if (!state)
90 return;
91 plane->state = &state->base;
92 }
93
94 state->base.plane = plane;
95 state->pending.format = DRM_FORMAT_RGB565;
96}
97
98static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
99{
100 struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state);
101 struct mtk_plane_state *state;
102
103 state = kzalloc(sizeof(*state), GFP_KERNEL);
104 if (!state)
105 return NULL;
106
107 __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
108
109 WARN_ON(state->base.plane != plane);
110
111 state->pending = old_state->pending;
112
113 return &state->base;
114}
115
116static void mtk_drm_plane_destroy_state(struct drm_plane *plane,
117 struct drm_plane_state *state)
118{
119 __drm_atomic_helper_plane_destroy_state(plane, state);
120 kfree(to_mtk_plane_state(state));
121}
122
123static const struct drm_plane_funcs mtk_plane_funcs = {
124 .update_plane = drm_atomic_helper_update_plane,
125 .disable_plane = drm_atomic_helper_disable_plane,
126 .destroy = drm_plane_cleanup,
127 .reset = mtk_plane_reset,
128 .atomic_duplicate_state = mtk_plane_duplicate_state,
129 .atomic_destroy_state = mtk_drm_plane_destroy_state,
130};
131
132static int mtk_plane_atomic_check(struct drm_plane *plane,
133 struct drm_plane_state *state)
134{
135 struct drm_framebuffer *fb = state->fb;
136 struct drm_crtc_state *crtc_state;
137 bool visible;
138 struct drm_rect dest = {
139 .x1 = state->crtc_x,
140 .y1 = state->crtc_y,
141 .x2 = state->crtc_x + state->crtc_w,
142 .y2 = state->crtc_y + state->crtc_h,
143 };
144 struct drm_rect src = {
145 /* 16.16 fixed point */
146 .x1 = state->src_x,
147 .y1 = state->src_y,
148 .x2 = state->src_x + state->src_w,
149 .y2 = state->src_y + state->src_h,
150 };
151 struct drm_rect clip = { 0, };
152
153 if (!fb)
154 return 0;
155
156 if (!mtk_fb_get_gem_obj(fb)) {
157 DRM_DEBUG_KMS("buffer is null\n");
158 return -EFAULT;
159 }
160
161 if (!state->crtc)
162 return 0;
163
164 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
165 if (IS_ERR(crtc_state))
166 return PTR_ERR(crtc_state);
167
168 clip.x2 = crtc_state->mode.hdisplay;
169 clip.y2 = crtc_state->mode.vdisplay;
170
171 return drm_plane_helper_check_update(plane, state->crtc, fb,
172 &src, &dest, &clip,
173 DRM_PLANE_HELPER_NO_SCALING,
174 DRM_PLANE_HELPER_NO_SCALING,
175 true, true, &visible);
176}
177
178static void mtk_plane_atomic_update(struct drm_plane *plane,
179 struct drm_plane_state *old_state)
180{
181 struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
182 struct drm_crtc *crtc = state->base.crtc;
183 struct drm_gem_object *gem;
184 struct mtk_drm_gem_obj *mtk_gem;
185 struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
186 struct drm_rect dest = {
187 .x1 = state->base.crtc_x,
188 .y1 = state->base.crtc_y,
189 .x2 = state->base.crtc_x + state->base.crtc_w,
190 .y2 = state->base.crtc_y + state->base.crtc_h,
191 };
192 struct drm_rect clip = { 0, };
193
194 if (!crtc)
195 return;
196
197 clip.x2 = state->base.crtc->state->mode.hdisplay;
198 clip.y2 = state->base.crtc->state->mode.vdisplay;
199 drm_rect_intersect(&dest, &clip);
200
201 gem = mtk_fb_get_gem_obj(state->base.fb);
202 mtk_gem = to_mtk_gem_obj(gem);
203 mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest);
204}
205
206static void mtk_plane_atomic_disable(struct drm_plane *plane,
207 struct drm_plane_state *old_state)
208{
209 struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
210
211 state->pending.enable = false;
212 wmb(); /* Make sure the above parameter is set before update */
213 state->pending.dirty = true;
214}
215
216static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
217 .atomic_check = mtk_plane_atomic_check,
218 .atomic_update = mtk_plane_atomic_update,
219 .atomic_disable = mtk_plane_atomic_disable,
220};
221
222int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
223 unsigned long possible_crtcs, enum drm_plane_type type,
224 unsigned int zpos)
225{
226 int err;
227
228 err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
229 &mtk_plane_funcs, formats,
230 ARRAY_SIZE(formats), type, NULL);
231 if (err) {
232 DRM_ERROR("failed to initialize plane\n");
233 return err;
234 }
235
236 drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs);
237 mtk_plane->idx = zpos;
238
239 return 0;
240}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.h b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
new file mode 100644
index 000000000000..72a7b3e4c126
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h
@@ -0,0 +1,59 @@
1/*
2 * Copyright (c) 2015 MediaTek Inc.
3 * Author: CK Hu <ck.hu@mediatek.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#ifndef _MTK_DRM_PLANE_H_
16#define _MTK_DRM_PLANE_H_
17
18#include <drm/drm_crtc.h>
19#include <linux/types.h>
20
21struct mtk_drm_plane {
22 struct drm_plane base;
23 unsigned int idx;
24};
25
26struct mtk_plane_pending_state {
27 bool config;
28 bool enable;
29 dma_addr_t addr;
30 unsigned int pitch;
31 unsigned int format;
32 unsigned int x;
33 unsigned int y;
34 unsigned int width;
35 unsigned int height;
36 bool dirty;
37};
38
39struct mtk_plane_state {
40 struct drm_plane_state base;
41 struct mtk_plane_pending_state pending;
42};
43
44static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane)
45{
46 return container_of(plane, struct mtk_drm_plane, base);
47}
48
49static inline struct mtk_plane_state *
50to_mtk_plane_state(struct drm_plane_state *state)
51{
52 return container_of(state, struct mtk_plane_state, base);
53}
54
55int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
56 unsigned long possible_crtcs, enum drm_plane_type type,
57 unsigned int zpos);
58
59#endif