xref: /linux/drivers/gpu/drm/sun4i/sun8i_mixer.c (revision 55d0969c451159cff86949b38c39171cab962069)
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 drm_plane_state *plane_state;
278 	struct drm_plane *plane;
279 	u32 route = 0, pipe_en = 0;
280 
281 	DRM_DEBUG_DRIVER("Committing changes\n");
282 
283 	drm_for_each_plane(plane, state->dev) {
284 		struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
285 		bool enable;
286 		int zpos;
287 
288 		if (!(plane->possible_crtcs & drm_crtc_mask(crtc)) || layer->mixer != mixer)
289 			continue;
290 
291 		plane_state = drm_atomic_get_new_plane_state(state, plane);
292 		if (!plane_state)
293 			plane_state = plane->state;
294 
295 		enable = plane_state->crtc && plane_state->visible;
296 		zpos = plane_state->normalized_zpos;
297 
298 		DRM_DEBUG_DRIVER("  plane %d: chan=%d ovl=%d en=%d zpos=%d\n",
299 				 plane->base.id, layer->channel, layer->overlay,
300 				 enable, zpos);
301 
302 		/*
303 		 * We always update the layer enable bit, because it can clear
304 		 * spontaneously for unknown reasons.
305 		 */
306 		sun8i_layer_enable(layer, enable);
307 
308 		if (!enable)
309 			continue;
310 
311 		/* Route layer to pipe based on zpos */
312 		route |= layer->channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
313 		pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
314 	}
315 
316 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route);
317 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
318 		     pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
319 
320 	regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
321 		     SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
322 }
323 
324 static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
325 					    struct sunxi_engine *engine)
326 {
327 	struct drm_plane **planes;
328 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
329 	int i;
330 
331 	planes = devm_kcalloc(drm->dev,
332 			      mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
333 			      sizeof(*planes), GFP_KERNEL);
334 	if (!planes)
335 		return ERR_PTR(-ENOMEM);
336 
337 	for (i = 0; i < mixer->cfg->vi_num; i++) {
338 		struct sun8i_layer *layer;
339 
340 		layer = sun8i_vi_layer_init_one(drm, mixer, i);
341 		if (IS_ERR(layer)) {
342 			dev_err(drm->dev,
343 				"Couldn't initialize overlay plane\n");
344 			return ERR_CAST(layer);
345 		}
346 
347 		planes[i] = &layer->plane;
348 	}
349 
350 	for (i = 0; i < mixer->cfg->ui_num; i++) {
351 		struct sun8i_layer *layer;
352 
353 		layer = sun8i_ui_layer_init_one(drm, mixer, i);
354 		if (IS_ERR(layer)) {
355 			dev_err(drm->dev, "Couldn't initialize %s plane\n",
356 				i ? "overlay" : "primary");
357 			return ERR_CAST(layer);
358 		}
359 
360 		planes[mixer->cfg->vi_num + i] = &layer->plane;
361 	}
362 
363 	return planes;
364 }
365 
366 static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
367 				 const struct drm_display_mode *mode)
368 {
369 	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
370 	u32 bld_base, size, val;
371 	bool interlaced;
372 
373 	bld_base = sun8i_blender_base(mixer);
374 	interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
375 	size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
376 
377 	DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
378 			 mode->hdisplay, mode->vdisplay);
379 
380 	regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size);
381 	regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
382 
383 	if (interlaced)
384 		val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
385 	else
386 		val = 0;
387 
388 	regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
389 			   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
390 
391 	DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
392 			 interlaced ? "on" : "off");
393 }
394 
395 static const struct sunxi_engine_ops sun8i_engine_ops = {
396 	.commit		= sun8i_mixer_commit,
397 	.layers_init	= sun8i_layers_init,
398 	.mode_set	= sun8i_mixer_mode_set,
399 };
400 
401 static const struct regmap_config sun8i_mixer_regmap_config = {
402 	.reg_bits	= 32,
403 	.val_bits	= 32,
404 	.reg_stride	= 4,
405 	.max_register	= 0xffffc, /* guessed */
406 };
407 
408 static int sun8i_mixer_of_get_id(struct device_node *node)
409 {
410 	struct device_node *ep, *remote;
411 	struct of_endpoint of_ep;
412 
413 	/* Output port is 1, and we want the first endpoint. */
414 	ep = of_graph_get_endpoint_by_regs(node, 1, -1);
415 	if (!ep)
416 		return -EINVAL;
417 
418 	remote = of_graph_get_remote_endpoint(ep);
419 	of_node_put(ep);
420 	if (!remote)
421 		return -EINVAL;
422 
423 	of_graph_parse_endpoint(remote, &of_ep);
424 	of_node_put(remote);
425 	return of_ep.id;
426 }
427 
428 static int sun8i_mixer_bind(struct device *dev, struct device *master,
429 			      void *data)
430 {
431 	struct platform_device *pdev = to_platform_device(dev);
432 	struct drm_device *drm = data;
433 	struct sun4i_drv *drv = drm->dev_private;
434 	struct sun8i_mixer *mixer;
435 	void __iomem *regs;
436 	unsigned int base;
437 	int plane_cnt;
438 	int i, ret;
439 
440 	/*
441 	 * The mixer uses single 32-bit register to store memory
442 	 * addresses, so that it cannot deal with 64-bit memory
443 	 * addresses.
444 	 * Restrict the DMA mask so that the mixer won't be
445 	 * allocated some memory that is too high.
446 	 */
447 	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
448 	if (ret) {
449 		dev_err(dev, "Cannot do 32-bit DMA.\n");
450 		return ret;
451 	}
452 
453 	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
454 	if (!mixer)
455 		return -ENOMEM;
456 	dev_set_drvdata(dev, mixer);
457 	mixer->engine.ops = &sun8i_engine_ops;
458 	mixer->engine.node = dev->of_node;
459 
460 	if (of_property_present(dev->of_node, "iommus")) {
461 		/*
462 		 * This assume we have the same DMA constraints for
463 		 * all our the mixers in our pipeline. This sounds
464 		 * bad, but it has always been the case for us, and
465 		 * DRM doesn't do per-device allocation either, so we
466 		 * would need to fix DRM first...
467 		 */
468 		ret = of_dma_configure(drm->dev, dev->of_node, true);
469 		if (ret)
470 			return ret;
471 	}
472 
473 	/*
474 	 * While this function can fail, we shouldn't do anything
475 	 * if this happens. Some early DE2 DT entries don't provide
476 	 * mixer id but work nevertheless because matching between
477 	 * TCON and mixer is done by comparing node pointers (old
478 	 * way) instead comparing ids. If this function fails and
479 	 * id is needed, it will fail during id matching anyway.
480 	 */
481 	mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node);
482 
483 	mixer->cfg = of_device_get_match_data(dev);
484 	if (!mixer->cfg)
485 		return -EINVAL;
486 
487 	regs = devm_platform_ioremap_resource(pdev, 0);
488 	if (IS_ERR(regs))
489 		return PTR_ERR(regs);
490 
491 	mixer->engine.regs = devm_regmap_init_mmio(dev, regs,
492 						   &sun8i_mixer_regmap_config);
493 	if (IS_ERR(mixer->engine.regs)) {
494 		dev_err(dev, "Couldn't create the mixer regmap\n");
495 		return PTR_ERR(mixer->engine.regs);
496 	}
497 
498 	mixer->reset = devm_reset_control_get(dev, NULL);
499 	if (IS_ERR(mixer->reset)) {
500 		dev_err(dev, "Couldn't get our reset line\n");
501 		return PTR_ERR(mixer->reset);
502 	}
503 
504 	ret = reset_control_deassert(mixer->reset);
505 	if (ret) {
506 		dev_err(dev, "Couldn't deassert our reset line\n");
507 		return ret;
508 	}
509 
510 	mixer->bus_clk = devm_clk_get(dev, "bus");
511 	if (IS_ERR(mixer->bus_clk)) {
512 		dev_err(dev, "Couldn't get the mixer bus clock\n");
513 		ret = PTR_ERR(mixer->bus_clk);
514 		goto err_assert_reset;
515 	}
516 	clk_prepare_enable(mixer->bus_clk);
517 
518 	mixer->mod_clk = devm_clk_get(dev, "mod");
519 	if (IS_ERR(mixer->mod_clk)) {
520 		dev_err(dev, "Couldn't get the mixer module clock\n");
521 		ret = PTR_ERR(mixer->mod_clk);
522 		goto err_disable_bus_clk;
523 	}
524 
525 	/*
526 	 * It seems that we need to enforce that rate for whatever
527 	 * reason for the mixer to be functional. Make sure it's the
528 	 * case.
529 	 */
530 	if (mixer->cfg->mod_rate)
531 		clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate);
532 
533 	clk_prepare_enable(mixer->mod_clk);
534 
535 	list_add_tail(&mixer->engine.list, &drv->engine_list);
536 
537 	base = sun8i_blender_base(mixer);
538 
539 	/* Reset registers and disable unused sub-engines */
540 	if (mixer->cfg->is_de3) {
541 		for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4)
542 			regmap_write(mixer->engine.regs, i, 0);
543 
544 		regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0);
545 		regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0);
546 		regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0);
547 		regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0);
548 		regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0);
549 		regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0);
550 		regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0);
551 		regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0);
552 		regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0);
553 		regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0);
554 	} else {
555 		for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4)
556 			regmap_write(mixer->engine.regs, i, 0);
557 
558 		regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0);
559 		regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0);
560 		regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0);
561 		regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0);
562 		regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0);
563 		regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0);
564 		regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0);
565 	}
566 
567 	/* Enable the mixer */
568 	regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
569 		     SUN8I_MIXER_GLOBAL_CTL_RT_EN);
570 
571 	/* Set background color to black */
572 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
573 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
574 
575 	/*
576 	 * Set fill color of bottom plane to black. Generally not needed
577 	 * except when VI plane is at bottom (zpos = 0) and enabled.
578 	 */
579 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
580 		     SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
581 	regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
582 		     SUN8I_MIXER_BLEND_COLOR_BLACK);
583 
584 	plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
585 	for (i = 0; i < plane_cnt; i++)
586 		regmap_write(mixer->engine.regs,
587 			     SUN8I_MIXER_BLEND_MODE(base, i),
588 			     SUN8I_MIXER_BLEND_MODE_DEF);
589 
590 	regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
591 			   SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
592 
593 	return 0;
594 
595 err_disable_bus_clk:
596 	clk_disable_unprepare(mixer->bus_clk);
597 err_assert_reset:
598 	reset_control_assert(mixer->reset);
599 	return ret;
600 }
601 
602 static void sun8i_mixer_unbind(struct device *dev, struct device *master,
603 				 void *data)
604 {
605 	struct sun8i_mixer *mixer = dev_get_drvdata(dev);
606 
607 	list_del(&mixer->engine.list);
608 
609 	clk_disable_unprepare(mixer->mod_clk);
610 	clk_disable_unprepare(mixer->bus_clk);
611 	reset_control_assert(mixer->reset);
612 }
613 
614 static const struct component_ops sun8i_mixer_ops = {
615 	.bind	= sun8i_mixer_bind,
616 	.unbind	= sun8i_mixer_unbind,
617 };
618 
619 static int sun8i_mixer_probe(struct platform_device *pdev)
620 {
621 	return component_add(&pdev->dev, &sun8i_mixer_ops);
622 }
623 
624 static void sun8i_mixer_remove(struct platform_device *pdev)
625 {
626 	component_del(&pdev->dev, &sun8i_mixer_ops);
627 }
628 
629 static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
630 	.ccsc		= CCSC_MIXER0_LAYOUT,
631 	.scaler_mask	= 0xf,
632 	.scanline_yuv	= 2048,
633 	.ui_num		= 3,
634 	.vi_num		= 1,
635 };
636 
637 static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
638 	.ccsc		= CCSC_MIXER1_LAYOUT,
639 	.scaler_mask	= 0x3,
640 	.scanline_yuv	= 2048,
641 	.ui_num		= 1,
642 	.vi_num		= 1,
643 };
644 
645 static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
646 	.ccsc		= CCSC_MIXER0_LAYOUT,
647 	.mod_rate	= 432000000,
648 	.scaler_mask	= 0xf,
649 	.scanline_yuv	= 2048,
650 	.ui_num		= 3,
651 	.vi_num		= 1,
652 };
653 
654 static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
655 	.ccsc		= CCSC_MIXER0_LAYOUT,
656 	.mod_rate	= 297000000,
657 	.scaler_mask	= 0xf,
658 	.scanline_yuv	= 2048,
659 	.ui_num		= 3,
660 	.vi_num		= 1,
661 };
662 
663 static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
664 	.ccsc		= CCSC_MIXER1_LAYOUT,
665 	.mod_rate	= 297000000,
666 	.scaler_mask	= 0x3,
667 	.scanline_yuv	= 2048,
668 	.ui_num		= 1,
669 	.vi_num		= 1,
670 };
671 
672 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
673 	.vi_num = 2,
674 	.ui_num = 1,
675 	.scaler_mask = 0x3,
676 	.scanline_yuv = 2048,
677 	.ccsc = CCSC_MIXER0_LAYOUT,
678 	.mod_rate = 150000000,
679 };
680 
681 static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
682 	.ccsc		= CCSC_D1_MIXER0_LAYOUT,
683 	.mod_rate	= 297000000,
684 	.scaler_mask	= 0x3,
685 	.scanline_yuv	= 2048,
686 	.ui_num		= 1,
687 	.vi_num		= 1,
688 };
689 
690 static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
691 	.ccsc		= CCSC_MIXER1_LAYOUT,
692 	.mod_rate	= 297000000,
693 	.scaler_mask	= 0x1,
694 	.scanline_yuv	= 1024,
695 	.ui_num		= 0,
696 	.vi_num		= 1,
697 };
698 
699 static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
700 	.ccsc		= CCSC_MIXER0_LAYOUT,
701 	.mod_rate	= 297000000,
702 	.scaler_mask	= 0xf,
703 	.scanline_yuv	= 4096,
704 	.ui_num		= 3,
705 	.vi_num		= 1,
706 };
707 
708 static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
709 	.ccsc		= CCSC_MIXER1_LAYOUT,
710 	.mod_rate	= 297000000,
711 	.scaler_mask	= 0x3,
712 	.scanline_yuv	= 2048,
713 	.ui_num		= 1,
714 	.vi_num		= 1,
715 };
716 
717 static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
718 	.ccsc		= CCSC_MIXER0_LAYOUT,
719 	.is_de3		= true,
720 	.mod_rate	= 600000000,
721 	.scaler_mask	= 0xf,
722 	.scanline_yuv	= 4096,
723 	.ui_num		= 3,
724 	.vi_num		= 1,
725 };
726 
727 static const struct of_device_id sun8i_mixer_of_table[] = {
728 	{
729 		.compatible = "allwinner,sun8i-a83t-de2-mixer-0",
730 		.data = &sun8i_a83t_mixer0_cfg,
731 	},
732 	{
733 		.compatible = "allwinner,sun8i-a83t-de2-mixer-1",
734 		.data = &sun8i_a83t_mixer1_cfg,
735 	},
736 	{
737 		.compatible = "allwinner,sun8i-h3-de2-mixer-0",
738 		.data = &sun8i_h3_mixer0_cfg,
739 	},
740 	{
741 		.compatible = "allwinner,sun8i-r40-de2-mixer-0",
742 		.data = &sun8i_r40_mixer0_cfg,
743 	},
744 	{
745 		.compatible = "allwinner,sun8i-r40-de2-mixer-1",
746 		.data = &sun8i_r40_mixer1_cfg,
747 	},
748 	{
749 		.compatible = "allwinner,sun8i-v3s-de2-mixer",
750 		.data = &sun8i_v3s_mixer_cfg,
751 	},
752 	{
753 		.compatible = "allwinner,sun20i-d1-de2-mixer-0",
754 		.data = &sun20i_d1_mixer0_cfg,
755 	},
756 	{
757 		.compatible = "allwinner,sun20i-d1-de2-mixer-1",
758 		.data = &sun20i_d1_mixer1_cfg,
759 	},
760 	{
761 		.compatible = "allwinner,sun50i-a64-de2-mixer-0",
762 		.data = &sun50i_a64_mixer0_cfg,
763 	},
764 	{
765 		.compatible = "allwinner,sun50i-a64-de2-mixer-1",
766 		.data = &sun50i_a64_mixer1_cfg,
767 	},
768 	{
769 		.compatible = "allwinner,sun50i-h6-de3-mixer-0",
770 		.data = &sun50i_h6_mixer0_cfg,
771 	},
772 	{ }
773 };
774 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
775 
776 static struct platform_driver sun8i_mixer_platform_driver = {
777 	.probe		= sun8i_mixer_probe,
778 	.remove_new	= sun8i_mixer_remove,
779 	.driver		= {
780 		.name		= "sun8i-mixer",
781 		.of_match_table	= sun8i_mixer_of_table,
782 	},
783 };
784 module_platform_driver(sun8i_mixer_platform_driver);
785 
786 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
787 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
788 MODULE_LICENSE("GPL");
789