1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Based on sun4i_backend.c, which is:
6*4882a593Smuzhiyun * Copyright (C) 2015 Free Electrons
7*4882a593Smuzhiyun * Copyright (C) 2015 NextThing Co
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/component.h>
11*4882a593Smuzhiyun #include <linux/dma-mapping.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/of_device.h>
14*4882a593Smuzhiyun #include <linux/of_graph.h>
15*4882a593Smuzhiyun #include <linux/reset.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
18*4882a593Smuzhiyun #include <drm/drm_crtc.h>
19*4882a593Smuzhiyun #include <drm/drm_fb_cma_helper.h>
20*4882a593Smuzhiyun #include <drm/drm_gem_cma_helper.h>
21*4882a593Smuzhiyun #include <drm/drm_plane_helper.h>
22*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include "sun4i_drv.h"
25*4882a593Smuzhiyun #include "sun8i_mixer.h"
26*4882a593Smuzhiyun #include "sun8i_ui_layer.h"
27*4882a593Smuzhiyun #include "sun8i_vi_layer.h"
28*4882a593Smuzhiyun #include "sunxi_engine.h"
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun struct de2_fmt_info {
31*4882a593Smuzhiyun u32 drm_fmt;
32*4882a593Smuzhiyun u32 de2_fmt;
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static const struct de2_fmt_info de2_formats[] = {
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ARGB8888,
38*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888,
39*4882a593Smuzhiyun },
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ABGR8888,
42*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888,
43*4882a593Smuzhiyun },
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBA8888,
46*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888,
47*4882a593Smuzhiyun },
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRA8888,
50*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888,
51*4882a593Smuzhiyun },
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_XRGB8888,
54*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888,
55*4882a593Smuzhiyun },
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_XBGR8888,
58*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888,
59*4882a593Smuzhiyun },
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBX8888,
62*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888,
63*4882a593Smuzhiyun },
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRX8888,
66*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888,
67*4882a593Smuzhiyun },
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGB888,
70*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
71*4882a593Smuzhiyun },
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGR888,
74*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGR888,
75*4882a593Smuzhiyun },
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGB565,
78*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGB565,
79*4882a593Smuzhiyun },
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGR565,
82*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGR565,
83*4882a593Smuzhiyun },
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ARGB4444,
86*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
87*4882a593Smuzhiyun },
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
90*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_XRGB4444,
91*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
92*4882a593Smuzhiyun },
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ABGR4444,
95*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
96*4882a593Smuzhiyun },
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
99*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_XBGR4444,
100*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
101*4882a593Smuzhiyun },
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBA4444,
104*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
105*4882a593Smuzhiyun },
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
108*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBX4444,
109*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
110*4882a593Smuzhiyun },
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRA4444,
113*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
114*4882a593Smuzhiyun },
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
117*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRX4444,
118*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
119*4882a593Smuzhiyun },
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ARGB1555,
122*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
123*4882a593Smuzhiyun },
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
126*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_XRGB1555,
127*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
128*4882a593Smuzhiyun },
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ABGR1555,
131*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
132*4882a593Smuzhiyun },
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
135*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_XBGR1555,
136*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
137*4882a593Smuzhiyun },
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBA5551,
140*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
141*4882a593Smuzhiyun },
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
144*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBX5551,
145*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
146*4882a593Smuzhiyun },
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRA5551,
149*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
150*4882a593Smuzhiyun },
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun /* for DE2 VI layer which ignores alpha */
153*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRX5551,
154*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
155*4882a593Smuzhiyun },
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ARGB2101010,
158*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ARGB2101010,
159*4882a593Smuzhiyun },
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_ABGR2101010,
162*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_ABGR2101010,
163*4882a593Smuzhiyun },
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_RGBA1010102,
166*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_RGBA1010102,
167*4882a593Smuzhiyun },
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_BGRA1010102,
170*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_BGRA1010102,
171*4882a593Smuzhiyun },
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_UYVY,
174*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_UYVY,
175*4882a593Smuzhiyun },
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_VYUY,
178*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_VYUY,
179*4882a593Smuzhiyun },
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YUYV,
182*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUYV,
183*4882a593Smuzhiyun },
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YVYU,
186*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YVYU,
187*4882a593Smuzhiyun },
188*4882a593Smuzhiyun {
189*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_NV16,
190*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_NV16,
191*4882a593Smuzhiyun },
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_NV61,
194*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_NV61,
195*4882a593Smuzhiyun },
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_NV12,
198*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_NV12,
199*4882a593Smuzhiyun },
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_NV21,
202*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_NV21,
203*4882a593Smuzhiyun },
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YUV422,
206*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
207*4882a593Smuzhiyun },
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YUV420,
210*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
211*4882a593Smuzhiyun },
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YUV411,
214*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
215*4882a593Smuzhiyun },
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YVU422,
218*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
219*4882a593Smuzhiyun },
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YVU420,
222*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
223*4882a593Smuzhiyun },
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_YVU411,
226*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
227*4882a593Smuzhiyun },
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_P010,
230*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_P010_YUV,
231*4882a593Smuzhiyun },
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun .drm_fmt = DRM_FORMAT_P210,
234*4882a593Smuzhiyun .de2_fmt = SUN8I_MIXER_FBFMT_P210_YUV,
235*4882a593Smuzhiyun },
236*4882a593Smuzhiyun };
237*4882a593Smuzhiyun
sun8i_mixer_drm_format_to_hw(u32 format,u32 * hw_format)238*4882a593Smuzhiyun int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun unsigned int i;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(de2_formats); ++i)
243*4882a593Smuzhiyun if (de2_formats[i].drm_fmt == format) {
244*4882a593Smuzhiyun *hw_format = de2_formats[i].de2_fmt;
245*4882a593Smuzhiyun return 0;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun return -EINVAL;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun
sun8i_mixer_commit(struct sunxi_engine * engine)251*4882a593Smuzhiyun static void sun8i_mixer_commit(struct sunxi_engine *engine)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun DRM_DEBUG_DRIVER("Committing changes\n");
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
256*4882a593Smuzhiyun SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
sun8i_layers_init(struct drm_device * drm,struct sunxi_engine * engine)259*4882a593Smuzhiyun static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
260*4882a593Smuzhiyun struct sunxi_engine *engine)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun struct drm_plane **planes;
263*4882a593Smuzhiyun struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
264*4882a593Smuzhiyun int i;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun planes = devm_kcalloc(drm->dev,
267*4882a593Smuzhiyun mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
268*4882a593Smuzhiyun sizeof(*planes), GFP_KERNEL);
269*4882a593Smuzhiyun if (!planes)
270*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun for (i = 0; i < mixer->cfg->vi_num; i++) {
273*4882a593Smuzhiyun struct sun8i_vi_layer *layer;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun layer = sun8i_vi_layer_init_one(drm, mixer, i);
276*4882a593Smuzhiyun if (IS_ERR(layer)) {
277*4882a593Smuzhiyun dev_err(drm->dev,
278*4882a593Smuzhiyun "Couldn't initialize overlay plane\n");
279*4882a593Smuzhiyun return ERR_CAST(layer);
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun planes[i] = &layer->plane;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun for (i = 0; i < mixer->cfg->ui_num; i++) {
286*4882a593Smuzhiyun struct sun8i_ui_layer *layer;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun layer = sun8i_ui_layer_init_one(drm, mixer, i);
289*4882a593Smuzhiyun if (IS_ERR(layer)) {
290*4882a593Smuzhiyun dev_err(drm->dev, "Couldn't initialize %s plane\n",
291*4882a593Smuzhiyun i ? "overlay" : "primary");
292*4882a593Smuzhiyun return ERR_CAST(layer);
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun planes[mixer->cfg->vi_num + i] = &layer->plane;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun return planes;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun static const struct sunxi_engine_ops sun8i_engine_ops = {
302*4882a593Smuzhiyun .commit = sun8i_mixer_commit,
303*4882a593Smuzhiyun .layers_init = sun8i_layers_init,
304*4882a593Smuzhiyun };
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun static const struct regmap_config sun8i_mixer_regmap_config = {
307*4882a593Smuzhiyun .reg_bits = 32,
308*4882a593Smuzhiyun .val_bits = 32,
309*4882a593Smuzhiyun .reg_stride = 4,
310*4882a593Smuzhiyun .max_register = 0xffffc, /* guessed */
311*4882a593Smuzhiyun };
312*4882a593Smuzhiyun
sun8i_mixer_of_get_id(struct device_node * node)313*4882a593Smuzhiyun static int sun8i_mixer_of_get_id(struct device_node *node)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun struct device_node *ep, *remote;
316*4882a593Smuzhiyun struct of_endpoint of_ep;
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun /* Output port is 1, and we want the first endpoint. */
319*4882a593Smuzhiyun ep = of_graph_get_endpoint_by_regs(node, 1, -1);
320*4882a593Smuzhiyun if (!ep)
321*4882a593Smuzhiyun return -EINVAL;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun remote = of_graph_get_remote_endpoint(ep);
324*4882a593Smuzhiyun of_node_put(ep);
325*4882a593Smuzhiyun if (!remote)
326*4882a593Smuzhiyun return -EINVAL;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun of_graph_parse_endpoint(remote, &of_ep);
329*4882a593Smuzhiyun of_node_put(remote);
330*4882a593Smuzhiyun return of_ep.id;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
sun8i_mixer_bind(struct device * dev,struct device * master,void * data)333*4882a593Smuzhiyun static int sun8i_mixer_bind(struct device *dev, struct device *master,
334*4882a593Smuzhiyun void *data)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun struct platform_device *pdev = to_platform_device(dev);
337*4882a593Smuzhiyun struct drm_device *drm = data;
338*4882a593Smuzhiyun struct sun4i_drv *drv = drm->dev_private;
339*4882a593Smuzhiyun struct sun8i_mixer *mixer;
340*4882a593Smuzhiyun struct resource *res;
341*4882a593Smuzhiyun void __iomem *regs;
342*4882a593Smuzhiyun unsigned int base;
343*4882a593Smuzhiyun int plane_cnt;
344*4882a593Smuzhiyun int i, ret;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun /*
347*4882a593Smuzhiyun * The mixer uses single 32-bit register to store memory
348*4882a593Smuzhiyun * addresses, so that it cannot deal with 64-bit memory
349*4882a593Smuzhiyun * addresses.
350*4882a593Smuzhiyun * Restrict the DMA mask so that the mixer won't be
351*4882a593Smuzhiyun * allocated some memory that is too high.
352*4882a593Smuzhiyun */
353*4882a593Smuzhiyun ret = dma_set_mask(dev, DMA_BIT_MASK(32));
354*4882a593Smuzhiyun if (ret) {
355*4882a593Smuzhiyun dev_err(dev, "Cannot do 32-bit DMA.\n");
356*4882a593Smuzhiyun return ret;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
360*4882a593Smuzhiyun if (!mixer)
361*4882a593Smuzhiyun return -ENOMEM;
362*4882a593Smuzhiyun dev_set_drvdata(dev, mixer);
363*4882a593Smuzhiyun mixer->engine.ops = &sun8i_engine_ops;
364*4882a593Smuzhiyun mixer->engine.node = dev->of_node;
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun if (of_find_property(dev->of_node, "iommus", NULL)) {
367*4882a593Smuzhiyun /*
368*4882a593Smuzhiyun * This assume we have the same DMA constraints for
369*4882a593Smuzhiyun * all our the mixers in our pipeline. This sounds
370*4882a593Smuzhiyun * bad, but it has always been the case for us, and
371*4882a593Smuzhiyun * DRM doesn't do per-device allocation either, so we
372*4882a593Smuzhiyun * would need to fix DRM first...
373*4882a593Smuzhiyun */
374*4882a593Smuzhiyun ret = of_dma_configure(drm->dev, dev->of_node, true);
375*4882a593Smuzhiyun if (ret)
376*4882a593Smuzhiyun return ret;
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun /*
380*4882a593Smuzhiyun * While this function can fail, we shouldn't do anything
381*4882a593Smuzhiyun * if this happens. Some early DE2 DT entries don't provide
382*4882a593Smuzhiyun * mixer id but work nevertheless because matching between
383*4882a593Smuzhiyun * TCON and mixer is done by comparing node pointers (old
384*4882a593Smuzhiyun * way) instead comparing ids. If this function fails and
385*4882a593Smuzhiyun * id is needed, it will fail during id matching anyway.
386*4882a593Smuzhiyun */
387*4882a593Smuzhiyun mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun mixer->cfg = of_device_get_match_data(dev);
390*4882a593Smuzhiyun if (!mixer->cfg)
391*4882a593Smuzhiyun return -EINVAL;
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
394*4882a593Smuzhiyun regs = devm_ioremap_resource(dev, res);
395*4882a593Smuzhiyun if (IS_ERR(regs))
396*4882a593Smuzhiyun return PTR_ERR(regs);
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun mixer->engine.regs = devm_regmap_init_mmio(dev, regs,
399*4882a593Smuzhiyun &sun8i_mixer_regmap_config);
400*4882a593Smuzhiyun if (IS_ERR(mixer->engine.regs)) {
401*4882a593Smuzhiyun dev_err(dev, "Couldn't create the mixer regmap\n");
402*4882a593Smuzhiyun return PTR_ERR(mixer->engine.regs);
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun mixer->reset = devm_reset_control_get(dev, NULL);
406*4882a593Smuzhiyun if (IS_ERR(mixer->reset)) {
407*4882a593Smuzhiyun dev_err(dev, "Couldn't get our reset line\n");
408*4882a593Smuzhiyun return PTR_ERR(mixer->reset);
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun ret = reset_control_deassert(mixer->reset);
412*4882a593Smuzhiyun if (ret) {
413*4882a593Smuzhiyun dev_err(dev, "Couldn't deassert our reset line\n");
414*4882a593Smuzhiyun return ret;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun mixer->bus_clk = devm_clk_get(dev, "bus");
418*4882a593Smuzhiyun if (IS_ERR(mixer->bus_clk)) {
419*4882a593Smuzhiyun dev_err(dev, "Couldn't get the mixer bus clock\n");
420*4882a593Smuzhiyun ret = PTR_ERR(mixer->bus_clk);
421*4882a593Smuzhiyun goto err_assert_reset;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun clk_prepare_enable(mixer->bus_clk);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun mixer->mod_clk = devm_clk_get(dev, "mod");
426*4882a593Smuzhiyun if (IS_ERR(mixer->mod_clk)) {
427*4882a593Smuzhiyun dev_err(dev, "Couldn't get the mixer module clock\n");
428*4882a593Smuzhiyun ret = PTR_ERR(mixer->mod_clk);
429*4882a593Smuzhiyun goto err_disable_bus_clk;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /*
433*4882a593Smuzhiyun * It seems that we need to enforce that rate for whatever
434*4882a593Smuzhiyun * reason for the mixer to be functional. Make sure it's the
435*4882a593Smuzhiyun * case.
436*4882a593Smuzhiyun */
437*4882a593Smuzhiyun if (mixer->cfg->mod_rate)
438*4882a593Smuzhiyun clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate);
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun clk_prepare_enable(mixer->mod_clk);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun list_add_tail(&mixer->engine.list, &drv->engine_list);
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun base = sun8i_blender_base(mixer);
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun /* Reset registers and disable unused sub-engines */
447*4882a593Smuzhiyun if (mixer->cfg->is_de3) {
448*4882a593Smuzhiyun for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4)
449*4882a593Smuzhiyun regmap_write(mixer->engine.regs, i, 0);
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0);
452*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0);
453*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0);
454*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0);
455*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0);
456*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0);
457*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0);
458*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0);
459*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0);
460*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0);
461*4882a593Smuzhiyun } else {
462*4882a593Smuzhiyun for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4)
463*4882a593Smuzhiyun regmap_write(mixer->engine.regs, i, 0);
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0);
466*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0);
467*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0);
468*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0);
469*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0);
470*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0);
471*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0);
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun /* Enable the mixer */
475*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
476*4882a593Smuzhiyun SUN8I_MIXER_GLOBAL_CTL_RT_EN);
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun /* Set background color to black */
479*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
480*4882a593Smuzhiyun SUN8I_MIXER_BLEND_COLOR_BLACK);
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun /*
483*4882a593Smuzhiyun * Set fill color of bottom plane to black. Generally not needed
484*4882a593Smuzhiyun * except when VI plane is at bottom (zpos = 0) and enabled.
485*4882a593Smuzhiyun */
486*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
487*4882a593Smuzhiyun SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
488*4882a593Smuzhiyun regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
489*4882a593Smuzhiyun SUN8I_MIXER_BLEND_COLOR_BLACK);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
492*4882a593Smuzhiyun for (i = 0; i < plane_cnt; i++)
493*4882a593Smuzhiyun regmap_write(mixer->engine.regs,
494*4882a593Smuzhiyun SUN8I_MIXER_BLEND_MODE(base, i),
495*4882a593Smuzhiyun SUN8I_MIXER_BLEND_MODE_DEF);
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
498*4882a593Smuzhiyun SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun return 0;
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun err_disable_bus_clk:
503*4882a593Smuzhiyun clk_disable_unprepare(mixer->bus_clk);
504*4882a593Smuzhiyun err_assert_reset:
505*4882a593Smuzhiyun reset_control_assert(mixer->reset);
506*4882a593Smuzhiyun return ret;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
sun8i_mixer_unbind(struct device * dev,struct device * master,void * data)509*4882a593Smuzhiyun static void sun8i_mixer_unbind(struct device *dev, struct device *master,
510*4882a593Smuzhiyun void *data)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun struct sun8i_mixer *mixer = dev_get_drvdata(dev);
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun list_del(&mixer->engine.list);
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun clk_disable_unprepare(mixer->mod_clk);
517*4882a593Smuzhiyun clk_disable_unprepare(mixer->bus_clk);
518*4882a593Smuzhiyun reset_control_assert(mixer->reset);
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun static const struct component_ops sun8i_mixer_ops = {
522*4882a593Smuzhiyun .bind = sun8i_mixer_bind,
523*4882a593Smuzhiyun .unbind = sun8i_mixer_unbind,
524*4882a593Smuzhiyun };
525*4882a593Smuzhiyun
sun8i_mixer_probe(struct platform_device * pdev)526*4882a593Smuzhiyun static int sun8i_mixer_probe(struct platform_device *pdev)
527*4882a593Smuzhiyun {
528*4882a593Smuzhiyun return component_add(&pdev->dev, &sun8i_mixer_ops);
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun
sun8i_mixer_remove(struct platform_device * pdev)531*4882a593Smuzhiyun static int sun8i_mixer_remove(struct platform_device *pdev)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun component_del(&pdev->dev, &sun8i_mixer_ops);
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun return 0;
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
539*4882a593Smuzhiyun .ccsc = 0,
540*4882a593Smuzhiyun .scaler_mask = 0xf,
541*4882a593Smuzhiyun .scanline_yuv = 2048,
542*4882a593Smuzhiyun .ui_num = 3,
543*4882a593Smuzhiyun .vi_num = 1,
544*4882a593Smuzhiyun };
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
547*4882a593Smuzhiyun .ccsc = 1,
548*4882a593Smuzhiyun .scaler_mask = 0x3,
549*4882a593Smuzhiyun .scanline_yuv = 2048,
550*4882a593Smuzhiyun .ui_num = 1,
551*4882a593Smuzhiyun .vi_num = 1,
552*4882a593Smuzhiyun };
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
555*4882a593Smuzhiyun .ccsc = 0,
556*4882a593Smuzhiyun .mod_rate = 432000000,
557*4882a593Smuzhiyun .scaler_mask = 0xf,
558*4882a593Smuzhiyun .scanline_yuv = 2048,
559*4882a593Smuzhiyun .ui_num = 3,
560*4882a593Smuzhiyun .vi_num = 1,
561*4882a593Smuzhiyun };
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
564*4882a593Smuzhiyun .ccsc = 0,
565*4882a593Smuzhiyun .mod_rate = 297000000,
566*4882a593Smuzhiyun .scaler_mask = 0xf,
567*4882a593Smuzhiyun .scanline_yuv = 2048,
568*4882a593Smuzhiyun .ui_num = 3,
569*4882a593Smuzhiyun .vi_num = 1,
570*4882a593Smuzhiyun };
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
573*4882a593Smuzhiyun .ccsc = 1,
574*4882a593Smuzhiyun .mod_rate = 297000000,
575*4882a593Smuzhiyun .scaler_mask = 0x3,
576*4882a593Smuzhiyun .scanline_yuv = 2048,
577*4882a593Smuzhiyun .ui_num = 1,
578*4882a593Smuzhiyun .vi_num = 1,
579*4882a593Smuzhiyun };
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
582*4882a593Smuzhiyun .vi_num = 2,
583*4882a593Smuzhiyun .ui_num = 1,
584*4882a593Smuzhiyun .scaler_mask = 0x3,
585*4882a593Smuzhiyun .scanline_yuv = 2048,
586*4882a593Smuzhiyun .ccsc = 0,
587*4882a593Smuzhiyun .mod_rate = 150000000,
588*4882a593Smuzhiyun };
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
591*4882a593Smuzhiyun .ccsc = 0,
592*4882a593Smuzhiyun .mod_rate = 297000000,
593*4882a593Smuzhiyun .scaler_mask = 0xf,
594*4882a593Smuzhiyun .scanline_yuv = 4096,
595*4882a593Smuzhiyun .ui_num = 3,
596*4882a593Smuzhiyun .vi_num = 1,
597*4882a593Smuzhiyun };
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
600*4882a593Smuzhiyun .ccsc = 1,
601*4882a593Smuzhiyun .mod_rate = 297000000,
602*4882a593Smuzhiyun .scaler_mask = 0x3,
603*4882a593Smuzhiyun .scanline_yuv = 2048,
604*4882a593Smuzhiyun .ui_num = 1,
605*4882a593Smuzhiyun .vi_num = 1,
606*4882a593Smuzhiyun };
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
609*4882a593Smuzhiyun .ccsc = 0,
610*4882a593Smuzhiyun .is_de3 = true,
611*4882a593Smuzhiyun .mod_rate = 600000000,
612*4882a593Smuzhiyun .scaler_mask = 0xf,
613*4882a593Smuzhiyun .scanline_yuv = 4096,
614*4882a593Smuzhiyun .ui_num = 3,
615*4882a593Smuzhiyun .vi_num = 1,
616*4882a593Smuzhiyun };
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun static const struct of_device_id sun8i_mixer_of_table[] = {
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun .compatible = "allwinner,sun8i-a83t-de2-mixer-0",
621*4882a593Smuzhiyun .data = &sun8i_a83t_mixer0_cfg,
622*4882a593Smuzhiyun },
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun .compatible = "allwinner,sun8i-a83t-de2-mixer-1",
625*4882a593Smuzhiyun .data = &sun8i_a83t_mixer1_cfg,
626*4882a593Smuzhiyun },
627*4882a593Smuzhiyun {
628*4882a593Smuzhiyun .compatible = "allwinner,sun8i-h3-de2-mixer-0",
629*4882a593Smuzhiyun .data = &sun8i_h3_mixer0_cfg,
630*4882a593Smuzhiyun },
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun .compatible = "allwinner,sun8i-r40-de2-mixer-0",
633*4882a593Smuzhiyun .data = &sun8i_r40_mixer0_cfg,
634*4882a593Smuzhiyun },
635*4882a593Smuzhiyun {
636*4882a593Smuzhiyun .compatible = "allwinner,sun8i-r40-de2-mixer-1",
637*4882a593Smuzhiyun .data = &sun8i_r40_mixer1_cfg,
638*4882a593Smuzhiyun },
639*4882a593Smuzhiyun {
640*4882a593Smuzhiyun .compatible = "allwinner,sun8i-v3s-de2-mixer",
641*4882a593Smuzhiyun .data = &sun8i_v3s_mixer_cfg,
642*4882a593Smuzhiyun },
643*4882a593Smuzhiyun {
644*4882a593Smuzhiyun .compatible = "allwinner,sun50i-a64-de2-mixer-0",
645*4882a593Smuzhiyun .data = &sun50i_a64_mixer0_cfg,
646*4882a593Smuzhiyun },
647*4882a593Smuzhiyun {
648*4882a593Smuzhiyun .compatible = "allwinner,sun50i-a64-de2-mixer-1",
649*4882a593Smuzhiyun .data = &sun50i_a64_mixer1_cfg,
650*4882a593Smuzhiyun },
651*4882a593Smuzhiyun {
652*4882a593Smuzhiyun .compatible = "allwinner,sun50i-h6-de3-mixer-0",
653*4882a593Smuzhiyun .data = &sun50i_h6_mixer0_cfg,
654*4882a593Smuzhiyun },
655*4882a593Smuzhiyun { }
656*4882a593Smuzhiyun };
657*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun static struct platform_driver sun8i_mixer_platform_driver = {
660*4882a593Smuzhiyun .probe = sun8i_mixer_probe,
661*4882a593Smuzhiyun .remove = sun8i_mixer_remove,
662*4882a593Smuzhiyun .driver = {
663*4882a593Smuzhiyun .name = "sun8i-mixer",
664*4882a593Smuzhiyun .of_match_table = sun8i_mixer_of_table,
665*4882a593Smuzhiyun },
666*4882a593Smuzhiyun };
667*4882a593Smuzhiyun module_platform_driver(sun8i_mixer_platform_driver);
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
670*4882a593Smuzhiyun MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
671*4882a593Smuzhiyun MODULE_LICENSE("GPL");
672