File Index
Files and directories
Sign in
FILE

sun8i_mixer.c

File preview with syntax highlighting.

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