xref: /linux/drivers/gpu/drm/sun4i/sun8i_mixer.c (revision 22c55fb9eb92395d999b8404d73e58540d11bdd8)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
4  *
5  * Based on sun4i_backend.c, which is:
6  *   Copyright (C) 2015 Free Electrons
7  *   Copyright (C) 2015 NextThing Co
8  */
9 
10 #include <linux/component.h>
11 #include <linux/dma-mapping.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_device.h>
15 #include <linux/of_graph.h>
16 #include <linux/platform_device.h>
17 #include <linux/reset.h>
18 
19 #include <drm/drm_atomic.h>
20 #include <drm/drm_atomic_helper.h>
21 #include <drm/drm_crtc.h>
22 #include <drm/drm_framebuffer.h>
23 #include <drm/drm_gem_dma_helper.h>
24 #include <drm/drm_probe_helper.h>
25 
26 #include "sun4i_drv.h"
27 #include "sun8i_mixer.h"
28 #include "sun8i_ui_layer.h"
29 #include "sun8i_vi_layer.h"
30 #include "sunxi_engine.h"
31 
32 struct de2_fmt_info {
33 	u32	drm_fmt;
34 	u32	de2_fmt;
35 };
36 
37 static const struct de2_fmt_info de2_formats[] = {
38 	{
39 		.drm_fmt = DRM_FORMAT_ARGB8888,
40 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888,
41 	},
42 	{
43 		.drm_fmt = DRM_FORMAT_ABGR8888,
44 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888,
45 	},
46 	{
47 		.drm_fmt = DRM_FORMAT_RGBA8888,
48 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888,
49 	},
50 	{
51 		.drm_fmt = DRM_FORMAT_BGRA8888,
52 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888,
53 	},
54 	{
55 		.drm_fmt = DRM_FORMAT_XRGB8888,
56 		.de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888,
57 	},
58 	{
59 		.drm_fmt = DRM_FORMAT_XBGR8888,
60 		.de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888,
61 	},
62 	{
63 		.drm_fmt = DRM_FORMAT_RGBX8888,
64 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888,
65 	},
66 	{
67 		.drm_fmt = DRM_FORMAT_BGRX8888,
68 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888,
69 	},
70 	{
71 		.drm_fmt = DRM_FORMAT_RGB888,
72 		.de2_fmt = SUN8I_MIXER_FBFMT_RGB888,
73 	},
74 	{
75 		.drm_fmt = DRM_FORMAT_BGR888,
76 		.de2_fmt = SUN8I_MIXER_FBFMT_BGR888,
77 	},
78 	{
79 		.drm_fmt = DRM_FORMAT_RGB565,
80 		.de2_fmt = SUN8I_MIXER_FBFMT_RGB565,
81 	},
82 	{
83 		.drm_fmt = DRM_FORMAT_BGR565,
84 		.de2_fmt = SUN8I_MIXER_FBFMT_BGR565,
85 	},
86 	{
87 		.drm_fmt = DRM_FORMAT_ARGB4444,
88 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
89 	},
90 	{
91 		/* for DE2 VI layer which ignores alpha */
92 		.drm_fmt = DRM_FORMAT_XRGB4444,
93 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444,
94 	},
95 	{
96 		.drm_fmt = DRM_FORMAT_ABGR4444,
97 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
98 	},
99 	{
100 		/* for DE2 VI layer which ignores alpha */
101 		.drm_fmt = DRM_FORMAT_XBGR4444,
102 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444,
103 	},
104 	{
105 		.drm_fmt = DRM_FORMAT_RGBA4444,
106 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
107 	},
108 	{
109 		/* for DE2 VI layer which ignores alpha */
110 		.drm_fmt = DRM_FORMAT_RGBX4444,
111 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444,
112 	},
113 	{
114 		.drm_fmt = DRM_FORMAT_BGRA4444,
115 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
116 	},
117 	{
118 		/* for DE2 VI layer which ignores alpha */
119 		.drm_fmt = DRM_FORMAT_BGRX4444,
120 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444,
121 	},
122 	{
123 		.drm_fmt = DRM_FORMAT_ARGB1555,
124 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
125 	},
126 	{
127 		/* for DE2 VI layer which ignores alpha */
128 		.drm_fmt = DRM_FORMAT_XRGB1555,
129 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555,
130 	},
131 	{
132 		.drm_fmt = DRM_FORMAT_ABGR1555,
133 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
134 	},
135 	{
136 		/* for DE2 VI layer which ignores alpha */
137 		.drm_fmt = DRM_FORMAT_XBGR1555,
138 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555,
139 	},
140 	{
141 		.drm_fmt = DRM_FORMAT_RGBA5551,
142 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
143 	},
144 	{
145 		/* for DE2 VI layer which ignores alpha */
146 		.drm_fmt = DRM_FORMAT_RGBX5551,
147 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551,
148 	},
149 	{
150 		.drm_fmt = DRM_FORMAT_BGRA5551,
151 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
152 	},
153 	{
154 		/* for DE2 VI layer which ignores alpha */
155 		.drm_fmt = DRM_FORMAT_BGRX5551,
156 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551,
157 	},
158 	{
159 		.drm_fmt = DRM_FORMAT_ARGB2101010,
160 		.de2_fmt = SUN8I_MIXER_FBFMT_ARGB2101010,
161 	},
162 	{
163 		.drm_fmt = DRM_FORMAT_ABGR2101010,
164 		.de2_fmt = SUN8I_MIXER_FBFMT_ABGR2101010,
165 	},
166 	{
167 		.drm_fmt = DRM_FORMAT_RGBA1010102,
168 		.de2_fmt = SUN8I_MIXER_FBFMT_RGBA1010102,
169 	},
170 	{
171 		.drm_fmt = DRM_FORMAT_BGRA1010102,
172 		.de2_fmt = SUN8I_MIXER_FBFMT_BGRA1010102,
173 	},
174 	{
175 		.drm_fmt = DRM_FORMAT_UYVY,
176 		.de2_fmt = SUN8I_MIXER_FBFMT_UYVY,
177 	},
178 	{
179 		.drm_fmt = DRM_FORMAT_VYUY,
180 		.de2_fmt = SUN8I_MIXER_FBFMT_VYUY,
181 	},
182 	{
183 		.drm_fmt = DRM_FORMAT_YUYV,
184 		.de2_fmt = SUN8I_MIXER_FBFMT_YUYV,
185 	},
186 	{
187 		.drm_fmt = DRM_FORMAT_YVYU,
188 		.de2_fmt = SUN8I_MIXER_FBFMT_YVYU,
189 	},
190 	{
191 		.drm_fmt = DRM_FORMAT_NV16,
192 		.de2_fmt = SUN8I_MIXER_FBFMT_NV16,
193 	},
194 	{
195 		.drm_fmt = DRM_FORMAT_NV61,
196 		.de2_fmt = SUN8I_MIXER_FBFMT_NV61,
197 	},
198 	{
199 		.drm_fmt = DRM_FORMAT_NV12,
200 		.de2_fmt = SUN8I_MIXER_FBFMT_NV12,
201 	},
202 	{
203 		.drm_fmt = DRM_FORMAT_NV21,
204 		.de2_fmt = SUN8I_MIXER_FBFMT_NV21,
205 	},
206 	{
207 		.drm_fmt = DRM_FORMAT_YUV422,
208 		.de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
209 	},
210 	{
211 		.drm_fmt = DRM_FORMAT_YUV420,
212 		.de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
213 	},
214 	{
215 		.drm_fmt = DRM_FORMAT_YUV411,
216 		.de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
217 	},
218 	{
219 		.drm_fmt = DRM_FORMAT_YVU422,
220 		.de2_fmt = SUN8I_MIXER_FBFMT_YUV422,
221 	},
222 	{
223 		.drm_fmt = DRM_FORMAT_YVU420,
224 		.de2_fmt = SUN8I_MIXER_FBFMT_YUV420,
225 	},
226 	{
227 		.drm_fmt = DRM_FORMAT_YVU411,
228 		.de2_fmt = SUN8I_MIXER_FBFMT_YUV411,
229 	},
230 	{
231 		.drm_fmt = DRM_FORMAT_P010,
232 		.de2_fmt = SUN8I_MIXER_FBFMT_P010_YUV,
233 	},
234 	{
235 		.drm_fmt = DRM_FORMAT_P210,
236 		.de2_fmt = SUN8I_MIXER_FBFMT_P210_YUV,
237 	},
238 };
239 
240 int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format)
241 {
242 	unsigned int i;
243 
244 	for (i = 0; i < ARRAY_SIZE(de2_formats); ++i)
245 		if (de2_formats[i].drm_fmt == format) {
246 			*hw_format = de2_formats[i].de2_fmt;
247 			return 0;
248 		}
249 
250 	return -EINVAL;
251 }
252 
253 static void sun8i_layer_enable(struct sun8i_layer *layer, bool enable)
254 {
255 	u32 ch_base = sun8i_channel_base(layer->mixer, layer->channel);
256 	u32 val, reg, mask;
257 
258 	if (layer->type == SUN8I_LAYER_TYPE_UI) {
259 		val = enable ? SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN : 0;
260 		mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
261 		reg = SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay);
262 	} else {
263 		val = enable ? SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN : 0;
264 		mask = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
265 		reg = SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay);
266 	}
267 
268 	regmap_update_bits(layer->mixer->engine.regs, reg, mask, val);
269 }
270 
271 static void sun8i_mixer_commit(struct sunxi_engine *engine,
272 			       struct drm_crtc *crtc,
273 			       struct drm_atomic_state *state)
274 {
275 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
276 	u32 bld_base = sun8i_blender_base(mixer);
277 	struct regmap *bld_regs = sun8i_blender_regmap(mixer);
278 	struct drm_plane_state *plane_state;
279 	struct drm_plane *plane;
280 	u32 route = 0, pipe_en = 0;
281 
282 	DRM_DEBUG_DRIVER("Committing changes\n");
283 
284 	drm_for_each_plane(plane, state->dev) {
285 		struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
286 		bool enable;
287 		int zpos;
288 
289 		if (!(plane->possible_crtcs & drm_crtc_mask(crtc)) || layer->mixer != mixer)
290 			continue;
291 
292 		plane_state = drm_atomic_get_new_plane_state(state, plane);
293 		if (!plane_state)
294 			plane_state = plane->state;
295 
296 		enable = plane_state->crtc && plane_state->visible;
297 		zpos = plane_state->normalized_zpos;
298 
299 		DRM_DEBUG_DRIVER("  plane %d: chan=%d ovl=%d en=%d zpos=%d\n",
300 				 plane->base.id, layer->channel, layer->overlay,
301 				 enable, zpos);
302 
303 		/*
304 		 * We always update the layer enable bit, because it can clear
305 		 * spontaneously for unknown reasons.
306 		 */
307 		sun8i_layer_enable(layer, enable);
308 
309 		if (!enable)
310 			continue;
311 
312 		/* Route layer to pipe based on zpos */
313 		route |= layer->channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
314 		pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
315 	}
316 
317 	regmap_write(bld_regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route);
318 	regmap_write(bld_regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
319 		     pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
320 
321 	if (mixer->cfg->de_type != SUN8I_MIXER_DE33)
322 		regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
323 			     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
324 }
325 
326 static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
327 					    struct sunxi_engine *engine)
328 {
329 	struct drm_plane **planes;
330 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
331 	int i;
332 
333 	planes = devm_kcalloc(drm->dev,
334 			      mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
335 			      sizeof(*planes), GFP_KERNEL);
336 	if (!planes)
337 		return ERR_PTR(-ENOMEM);
338 
339 	for (i = 0; i < mixer->cfg->vi_num; i++) {
340 		struct sun8i_layer *layer;
341 
342 		layer = sun8i_vi_layer_init_one(drm, mixer, i);
343 		if (IS_ERR(layer)) {
344 			dev_err(drm->dev,
345 				"Couldn't initialize overlay plane\n");
346 			return ERR_CAST(layer);
347 		}
348 
349 		planes[i] = &layer->plane;
350 	}
351 
352 	for (i = 0; i < mixer->cfg->ui_num; i++) {
353 		struct sun8i_layer *layer;
354 
355 		layer = sun8i_ui_layer_init_one(drm, mixer, i);
356 		if (IS_ERR(layer)) {
357 			dev_err(drm->dev, "Couldn't initialize %s plane\n",
358 				i ? "overlay" : "primary");
359 			return ERR_CAST(layer);
360 		}
361 
362 		planes[mixer->cfg->vi_num + i] = &layer->plane;
363 	}
364 
365 	return planes;
366 }
367 
368 static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
369 				 const struct drm_display_mode *mode)
370 {
371 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
372 	struct regmap *bld_regs;
373 	u32 bld_base, size, val;
374 	bool interlaced;
375 
376 	bld_base = sun8i_blender_base(mixer);
377 	bld_regs = sun8i_blender_regmap(mixer);
378 	interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
379 	size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
380 
381 	DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
382 			 mode->hdisplay, mode->vdisplay);
383 
384 	if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
385 		regmap_write(mixer->top_regs, SUN50I_MIXER_GLOBAL_SIZE, size);
386 	else
387 		regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, size);
388 
389 	regmap_write(bld_regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
390 
391 	if (interlaced)
392 		val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
393 	else
394 		val = 0;
395 
396 	regmap_update_bits(bld_regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
397 			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
398 
399 	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
400 			 interlaced ? "on" : "off");
401 }
402 
403 static const struct sunxi_engine_ops sun8i_engine_ops = {
404 	.commit		= sun8i_mixer_commit,
405 	.layers_init	= sun8i_layers_init,
406 	.mode_set	= sun8i_mixer_mode_set,
407 };
408 
409 static const struct regmap_config sun8i_mixer_regmap_config = {
410 	.name		= "layers",
411 	.reg_bits	= 32,
412 	.val_bits	= 32,
413 	.reg_stride	= 4,
414 	.max_register	= 0xffffc, /* guessed */
415 };
416 
417 static const struct regmap_config sun8i_top_regmap_config = {
418 	.name		= "top",
419 	.reg_bits	= 32,
420 	.val_bits	= 32,
421 	.reg_stride	= 4,
422 	.max_register	= 0x3c,
423 };
424 
425 static const struct regmap_config sun8i_disp_regmap_config = {
426 	.name		= "display",
427 	.reg_bits	= 32,
428 	.val_bits	= 32,
429 	.reg_stride	= 4,
430 	.max_register	= 0x20000,
431 };
432 
433 static int sun8i_mixer_of_get_id(struct device_node *node)
434 {
435 	struct device_node *ep, *remote;
436 	struct of_endpoint of_ep;
437 
438 	/* Output port is 1, and we want the first endpoint. */
439 	ep = of_graph_get_endpoint_by_regs(node, 1, -1);
440 	if (!ep)
441 		return -EINVAL;
442 
443 	remote = of_graph_get_remote_endpoint(ep);
444 	of_node_put(ep);
445 	if (!remote)
446 		return -EINVAL;
447 
448 	of_graph_parse_endpoint(remote, &of_ep);
449 	of_node_put(remote);
450 	return of_ep.id;
451 }
452 
453 static void sun8i_mixer_init(struct sun8i_mixer *mixer)
454 {
455 	struct regmap *top_regs, *disp_regs;
456 	unsigned int base = sun8i_blender_base(mixer);
457 	int plane_cnt, i;
458 
459 	if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
460 		top_regs = mixer->top_regs;
461 		disp_regs = mixer->disp_regs;
462 	} else {
463 		top_regs = mixer->engine.regs;
464 		disp_regs = mixer->engine.regs;
465 	}
466 
467 	/* Enable the mixer */
468 	regmap_write(top_regs, SUN8I_MIXER_GLOBAL_CTL,
469 		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
470 
471 	if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
472 		regmap_write(top_regs, SUN50I_MIXER_GLOBAL_CLK, 1);
473 
474 	/* Set background color to black */
475 	regmap_write(disp_regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
476 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
477 
478 	/*
479 	 * Set fill color of bottom plane to black. Generally not needed
480 	 * except when VI plane is at bottom (zpos = 0) and enabled.
481 	 */
482 	regmap_write(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
483 		     SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
484 	regmap_write(disp_regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
485 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
486 
487 	plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
488 	for (i = 0; i < plane_cnt; i++)
489 		regmap_write(disp_regs,
490 			     SUN8I_MIXER_BLEND_MODE(base, i),
491 			     SUN8I_MIXER_BLEND_MODE_DEF);
492 
493 	regmap_update_bits(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
494 			   SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
495 }
496 
497 static int sun8i_mixer_bind(struct device *dev, struct device *master,
498 			      void *data)
499 {
500 	struct platform_device *pdev = to_platform_device(dev);
501 	struct drm_device *drm = data;
502 	struct sun4i_drv *drv = drm->dev_private;
503 	struct sun8i_mixer *mixer;
504 	void __iomem *regs;
505 	int i, ret;
506 
507 	/*
508 	 * The mixer uses single 32-bit register to store memory
509 	 * addresses, so that it cannot deal with 64-bit memory
510 	 * addresses.
511 	 * Restrict the DMA mask so that the mixer won't be
512 	 * allocated some memory that is too high.
513 	 */
514 	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
515 	if (ret) {
516 		dev_err(dev, "Cannot do 32-bit DMA.\n");
517 		return ret;
518 	}
519 
520 	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
521 	if (!mixer)
522 		return -ENOMEM;
523 	dev_set_drvdata(dev, mixer);
524 	mixer->engine.ops = &sun8i_engine_ops;
525 	mixer->engine.node = dev->of_node;
526 
527 	if (of_property_present(dev->of_node, "iommus")) {
528 		/*
529 		 * This assume we have the same DMA constraints for
530 		 * all our the mixers in our pipeline. This sounds
531 		 * bad, but it has always been the case for us, and
532 		 * DRM doesn't do per-device allocation either, so we
533 		 * would need to fix DRM first...
534 		 */
535 		ret = of_dma_configure(drm->dev, dev->of_node, true);
536 		if (ret)
537 			return ret;
538 	}
539 
540 	/*
541 	 * While this function can fail, we shouldn't do anything
542 	 * if this happens. Some early DE2 DT entries don't provide
543 	 * mixer id but work nevertheless because matching between
544 	 * TCON and mixer is done by comparing node pointers (old
545 	 * way) instead comparing ids. If this function fails and
546 	 * id is needed, it will fail during id matching anyway.
547 	 */
548 	mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node);
549 
550 	mixer->cfg = of_device_get_match_data(dev);
551 	if (!mixer->cfg)
552 		return -EINVAL;
553 
554 	regs = devm_platform_ioremap_resource(pdev, 0);
555 	if (IS_ERR(regs))
556 		return PTR_ERR(regs);
557 
558 	mixer->engine.regs = devm_regmap_init_mmio(dev, regs,
559 						   &sun8i_mixer_regmap_config);
560 	if (IS_ERR(mixer->engine.regs)) {
561 		dev_err(dev, "Couldn't create the mixer regmap\n");
562 		return PTR_ERR(mixer->engine.regs);
563 	}
564 
565 	if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
566 		regs = devm_platform_ioremap_resource_byname(pdev, "top");
567 		if (IS_ERR(regs))
568 			return PTR_ERR(regs);
569 
570 		mixer->top_regs = devm_regmap_init_mmio(dev, regs,
571 							&sun8i_top_regmap_config);
572 		if (IS_ERR(mixer->top_regs)) {
573 			dev_err(dev, "Couldn't create the top regmap\n");
574 			return PTR_ERR(mixer->top_regs);
575 		}
576 
577 		regs = devm_platform_ioremap_resource_byname(pdev, "display");
578 		if (IS_ERR(regs))
579 			return PTR_ERR(regs);
580 
581 		mixer->disp_regs = devm_regmap_init_mmio(dev, regs,
582 							 &sun8i_disp_regmap_config);
583 		if (IS_ERR(mixer->disp_regs)) {
584 			dev_err(dev, "Couldn't create the disp regmap\n");
585 			return PTR_ERR(mixer->disp_regs);
586 		}
587 	}
588 
589 	mixer->reset = devm_reset_control_get(dev, NULL);
590 	if (IS_ERR(mixer->reset)) {
591 		dev_err(dev, "Couldn't get our reset line\n");
592 		return PTR_ERR(mixer->reset);
593 	}
594 
595 	ret = reset_control_deassert(mixer->reset);
596 	if (ret) {
597 		dev_err(dev, "Couldn't deassert our reset line\n");
598 		return ret;
599 	}
600 
601 	mixer->bus_clk = devm_clk_get(dev, "bus");
602 	if (IS_ERR(mixer->bus_clk)) {
603 		dev_err(dev, "Couldn't get the mixer bus clock\n");
604 		ret = PTR_ERR(mixer->bus_clk);
605 		goto err_assert_reset;
606 	}
607 	clk_prepare_enable(mixer->bus_clk);
608 
609 	mixer->mod_clk = devm_clk_get(dev, "mod");
610 	if (IS_ERR(mixer->mod_clk)) {
611 		dev_err(dev, "Couldn't get the mixer module clock\n");
612 		ret = PTR_ERR(mixer->mod_clk);
613 		goto err_disable_bus_clk;
614 	}
615 
616 	/*
617 	 * It seems that we need to enforce that rate for whatever
618 	 * reason for the mixer to be functional. Make sure it's the
619 	 * case.
620 	 */
621 	if (mixer->cfg->mod_rate)
622 		clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate);
623 
624 	clk_prepare_enable(mixer->mod_clk);
625 
626 	list_add_tail(&mixer->engine.list, &drv->engine_list);
627 
628 	/* Reset registers and disable unused sub-engines */
629 	if (mixer->cfg->de_type == SUN8I_MIXER_DE3) {
630 		for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4)
631 			regmap_write(mixer->engine.regs, i, 0);
632 
633 		regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0);
634 		regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0);
635 		regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0);
636 		regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0);
637 		regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0);
638 		regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0);
639 		regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0);
640 		regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0);
641 		regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0);
642 		regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0);
643 	} else if (mixer->cfg->de_type == SUN8I_MIXER_DE2) {
644 		for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4)
645 			regmap_write(mixer->engine.regs, i, 0);
646 
647 		regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0);
648 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0);
649 		regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0);
650 		regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0);
651 		regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0);
652 		regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0);
653 		regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0);
654 	}
655 
656 	sun8i_mixer_init(mixer);
657 
658 	return 0;
659 
660 err_disable_bus_clk:
661 	clk_disable_unprepare(mixer->bus_clk);
662 err_assert_reset:
663 	reset_control_assert(mixer->reset);
664 	return ret;
665 }
666 
667 static void sun8i_mixer_unbind(struct device *dev, struct device *master,
668 				 void *data)
669 {
670 	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
671 
672 	list_del(&mixer->engine.list);
673 
674 	clk_disable_unprepare(mixer->mod_clk);
675 	clk_disable_unprepare(mixer->bus_clk);
676 	reset_control_assert(mixer->reset);
677 }
678 
679 static const struct component_ops sun8i_mixer_ops = {
680 	.bind	= sun8i_mixer_bind,
681 	.unbind	= sun8i_mixer_unbind,
682 };
683 
684 static int sun8i_mixer_probe(struct platform_device *pdev)
685 {
686 	return component_add(&pdev->dev, &sun8i_mixer_ops);
687 }
688 
689 static void sun8i_mixer_remove(struct platform_device *pdev)
690 {
691 	component_del(&pdev->dev, &sun8i_mixer_ops);
692 }
693 
694 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
695 	.ccsc		= CCSC_MIXER0_LAYOUT,
696 	.de_type	= SUN8I_MIXER_DE2,
697 	.scaler_mask	= 0xf,
698 	.scanline_yuv	= 2048,
699 	.ui_num		= 3,
700 	.vi_num		= 1,
701 };
702 
703 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
704 	.ccsc		= CCSC_MIXER1_LAYOUT,
705 	.de_type	= SUN8I_MIXER_DE2,
706 	.scaler_mask	= 0x3,
707 	.scanline_yuv	= 2048,
708 	.ui_num		= 1,
709 	.vi_num		= 1,
710 };
711 
712 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
713 	.ccsc		= CCSC_MIXER0_LAYOUT,
714 	.de_type	= SUN8I_MIXER_DE2,
715 	.mod_rate	= 432000000,
716 	.scaler_mask	= 0xf,
717 	.scanline_yuv	= 2048,
718 	.ui_num		= 3,
719 	.vi_num		= 1,
720 };
721 
722 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
723 	.ccsc		= CCSC_MIXER0_LAYOUT,
724 	.de_type	= SUN8I_MIXER_DE2,
725 	.mod_rate	= 297000000,
726 	.scaler_mask	= 0xf,
727 	.scanline_yuv	= 2048,
728 	.ui_num		= 3,
729 	.vi_num		= 1,
730 };
731 
732 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
733 	.ccsc		= CCSC_MIXER1_LAYOUT,
734 	.de_type	= SUN8I_MIXER_DE2,
735 	.mod_rate	= 297000000,
736 	.scaler_mask	= 0x3,
737 	.scanline_yuv	= 2048,
738 	.ui_num		= 1,
739 	.vi_num		= 1,
740 };
741 
742 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
743 	.de_type = SUN8I_MIXER_DE2,
744 	.vi_num = 2,
745 	.ui_num = 1,
746 	.scaler_mask = 0x3,
747 	.scanline_yuv = 2048,
748 	.ccsc = CCSC_MIXER0_LAYOUT,
749 	.mod_rate = 150000000,
750 };
751 
752 static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
753 	.ccsc		= CCSC_D1_MIXER0_LAYOUT,
754 	.de_type	= SUN8I_MIXER_DE2,
755 	.mod_rate	= 297000000,
756 	.scaler_mask	= 0x3,
757 	.scanline_yuv	= 2048,
758 	.ui_num		= 1,
759 	.vi_num		= 1,
760 };
761 
762 static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
763 	.ccsc		= CCSC_MIXER1_LAYOUT,
764 	.de_type	= SUN8I_MIXER_DE2,
765 	.mod_rate	= 297000000,
766 	.scaler_mask	= 0x1,
767 	.scanline_yuv	= 1024,
768 	.ui_num		= 0,
769 	.vi_num		= 1,
770 };
771 
772 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
773 	.ccsc		= CCSC_MIXER0_LAYOUT,
774 	.de_type	= SUN8I_MIXER_DE2,
775 	.mod_rate	= 297000000,
776 	.scaler_mask	= 0xf,
777 	.scanline_yuv	= 4096,
778 	.ui_num		= 3,
779 	.vi_num		= 1,
780 };
781 
782 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
783 	.ccsc		= CCSC_MIXER1_LAYOUT,
784 	.de_type	= SUN8I_MIXER_DE2,
785 	.mod_rate	= 297000000,
786 	.scaler_mask	= 0x3,
787 	.scanline_yuv	= 2048,
788 	.ui_num		= 1,
789 	.vi_num		= 1,
790 };
791 
792 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
793 	.ccsc		= CCSC_MIXER0_LAYOUT,
794 	.de_type	= SUN8I_MIXER_DE3,
795 	.mod_rate	= 600000000,
796 	.scaler_mask	= 0xf,
797 	.scanline_yuv	= 4096,
798 	.ui_num		= 3,
799 	.vi_num		= 1,
800 };
801 
802 static const struct sun8i_mixer_cfg sun50i_h616_mixer0_cfg = {
803 	.ccsc		= CCSC_MIXER0_LAYOUT,
804 	.de_type	= SUN8I_MIXER_DE33,
805 	.mod_rate	= 600000000,
806 	.scaler_mask	= 0xf,
807 	.scanline_yuv	= 4096,
808 	.ui_num		= 3,
809 	.vi_num		= 1,
810 	.map		= {0, 6, 7, 8},
811 };
812 
813 static const struct of_device_id sun8i_mixer_of_table[] = {
814 	{
815 		.compatible = "allwinner,sun8i-a83t-de2-mixer-0",
816 		.data = &sun8i_a83t_mixer0_cfg,
817 	},
818 	{
819 		.compatible = "allwinner,sun8i-a83t-de2-mixer-1",
820 		.data = &sun8i_a83t_mixer1_cfg,
821 	},
822 	{
823 		.compatible = "allwinner,sun8i-h3-de2-mixer-0",
824 		.data = &sun8i_h3_mixer0_cfg,
825 	},
826 	{
827 		.compatible = "allwinner,sun8i-r40-de2-mixer-0",
828 		.data = &sun8i_r40_mixer0_cfg,
829 	},
830 	{
831 		.compatible = "allwinner,sun8i-r40-de2-mixer-1",
832 		.data = &sun8i_r40_mixer1_cfg,
833 	},
834 	{
835 		.compatible = "allwinner,sun8i-v3s-de2-mixer",
836 		.data = &sun8i_v3s_mixer_cfg,
837 	},
838 	{
839 		.compatible = "allwinner,sun20i-d1-de2-mixer-0",
840 		.data = &sun20i_d1_mixer0_cfg,
841 	},
842 	{
843 		.compatible = "allwinner,sun20i-d1-de2-mixer-1",
844 		.data = &sun20i_d1_mixer1_cfg,
845 	},
846 	{
847 		.compatible = "allwinner,sun50i-a64-de2-mixer-0",
848 		.data = &sun50i_a64_mixer0_cfg,
849 	},
850 	{
851 		.compatible = "allwinner,sun50i-a64-de2-mixer-1",
852 		.data = &sun50i_a64_mixer1_cfg,
853 	},
854 	{
855 		.compatible = "allwinner,sun50i-h6-de3-mixer-0",
856 		.data = &sun50i_h6_mixer0_cfg,
857 	},
858 	{
859 		.compatible = "allwinner,sun50i-h616-de33-mixer-0",
860 		.data = &sun50i_h616_mixer0_cfg,
861 	},
862 	{ }
863 };
864 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
865 
866 static struct platform_driver sun8i_mixer_platform_driver = {
867 	.probe		= sun8i_mixer_probe,
868 	.remove		= sun8i_mixer_remove,
869 	.driver		= {
870 		.name		= "sun8i-mixer",
871 		.of_match_table	= sun8i_mixer_of_table,
872 	},
873 };
874 module_platform_driver(sun8i_mixer_platform_driver);
875 
876 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
877 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
878 MODULE_LICENSE("GPL");
879