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