1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> |
4 | */ |
5 | |
6 | #include <drm/drm_atomic.h> |
7 | #include <drm/drm_atomic_helper.h> |
8 | #include <drm/drm_blend.h> |
9 | #include <drm/drm_crtc.h> |
10 | #include <drm/drm_fb_dma_helper.h> |
11 | #include <drm/drm_framebuffer.h> |
12 | #include <drm/drm_gem_atomic_helper.h> |
13 | #include <drm/drm_gem_dma_helper.h> |
14 | #include <drm/drm_probe_helper.h> |
15 | |
16 | #include "sun8i_csc.h" |
17 | #include "sun8i_mixer.h" |
18 | #include "sun8i_vi_layer.h" |
19 | #include "sun8i_vi_scaler.h" |
20 | |
21 | static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, |
22 | int overlay, bool enable, unsigned int zpos, |
23 | unsigned int old_zpos) |
24 | { |
25 | u32 val, bld_base, ch_base; |
26 | |
27 | bld_base = sun8i_blender_base(mixer); |
28 | ch_base = sun8i_channel_base(mixer, channel); |
29 | |
30 | DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n" , |
31 | enable ? "En" : "Dis" , channel, overlay); |
32 | |
33 | if (enable) |
34 | val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; |
35 | else |
36 | val = 0; |
37 | |
38 | regmap_update_bits(map: mixer->engine.regs, |
39 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), |
40 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); |
41 | |
42 | if (!enable || zpos != old_zpos) { |
43 | regmap_update_bits(map: mixer->engine.regs, |
44 | SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), |
45 | SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), |
46 | val: 0); |
47 | |
48 | regmap_update_bits(map: mixer->engine.regs, |
49 | SUN8I_MIXER_BLEND_ROUTE(bld_base), |
50 | SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), |
51 | val: 0); |
52 | } |
53 | |
54 | if (enable) { |
55 | val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); |
56 | |
57 | regmap_update_bits(map: mixer->engine.regs, |
58 | SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), |
59 | mask: val, val); |
60 | |
61 | val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); |
62 | |
63 | regmap_update_bits(map: mixer->engine.regs, |
64 | SUN8I_MIXER_BLEND_ROUTE(bld_base), |
65 | SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), |
66 | val); |
67 | } |
68 | } |
69 | |
70 | static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, |
71 | int overlay, struct drm_plane *plane) |
72 | { |
73 | u32 mask, val, ch_base; |
74 | |
75 | ch_base = sun8i_channel_base(mixer, channel); |
76 | |
77 | if (mixer->cfg->is_de3) { |
78 | mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK | |
79 | SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK; |
80 | val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA |
81 | (plane->state->alpha >> 8); |
82 | |
83 | val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? |
84 | SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL : |
85 | SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED; |
86 | |
87 | regmap_update_bits(map: mixer->engine.regs, |
88 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, |
89 | overlay), |
90 | mask, val); |
91 | } else if (mixer->cfg->vi_num == 1) { |
92 | regmap_update_bits(map: mixer->engine.regs, |
93 | SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG, |
94 | SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK, |
95 | SUN8I_MIXER_FCC_GLOBAL_ALPHA |
96 | (plane->state->alpha >> 8)); |
97 | } |
98 | } |
99 | |
100 | static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, |
101 | int overlay, struct drm_plane *plane, |
102 | unsigned int zpos) |
103 | { |
104 | struct drm_plane_state *state = plane->state; |
105 | const struct drm_format_info *format = state->fb->format; |
106 | u32 src_w, src_h, dst_w, dst_h; |
107 | u32 bld_base, ch_base; |
108 | u32 outsize, insize; |
109 | u32 hphase, vphase; |
110 | u32 hn = 0, hm = 0; |
111 | u32 vn = 0, vm = 0; |
112 | bool subsampled; |
113 | |
114 | DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n" , |
115 | channel, overlay); |
116 | |
117 | bld_base = sun8i_blender_base(mixer); |
118 | ch_base = sun8i_channel_base(mixer, channel); |
119 | |
120 | src_w = drm_rect_width(r: &state->src) >> 16; |
121 | src_h = drm_rect_height(r: &state->src) >> 16; |
122 | dst_w = drm_rect_width(r: &state->dst); |
123 | dst_h = drm_rect_height(r: &state->dst); |
124 | |
125 | hphase = state->src.x1 & 0xffff; |
126 | vphase = state->src.y1 & 0xffff; |
127 | |
128 | /* make coordinates dividable by subsampling factor */ |
129 | if (format->hsub > 1) { |
130 | int mask, remainder; |
131 | |
132 | mask = format->hsub - 1; |
133 | remainder = (state->src.x1 >> 16) & mask; |
134 | src_w = (src_w + remainder) & ~mask; |
135 | hphase += remainder << 16; |
136 | } |
137 | |
138 | if (format->vsub > 1) { |
139 | int mask, remainder; |
140 | |
141 | mask = format->vsub - 1; |
142 | remainder = (state->src.y1 >> 16) & mask; |
143 | src_h = (src_h + remainder) & ~mask; |
144 | vphase += remainder << 16; |
145 | } |
146 | |
147 | insize = SUN8I_MIXER_SIZE(src_w, src_h); |
148 | outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); |
149 | |
150 | /* Set height and width */ |
151 | DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n" , |
152 | (state->src.x1 >> 16) & ~(format->hsub - 1), |
153 | (state->src.y1 >> 16) & ~(format->vsub - 1)); |
154 | DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n" , src_w, src_h); |
155 | regmap_write(map: mixer->engine.regs, |
156 | SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay), |
157 | val: insize); |
158 | regmap_write(map: mixer->engine.regs, |
159 | SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base), |
160 | val: insize); |
161 | |
162 | /* |
163 | * Scaler must be enabled for subsampled formats, so it scales |
164 | * chroma to same size as luma. |
165 | */ |
166 | subsampled = format->hsub > 1 || format->vsub > 1; |
167 | |
168 | if (insize != outsize || subsampled || hphase || vphase) { |
169 | unsigned int scanline, required; |
170 | struct drm_display_mode *mode; |
171 | u32 hscale, vscale, fps; |
172 | u64 ability; |
173 | |
174 | DRM_DEBUG_DRIVER("HW scaling is enabled\n" ); |
175 | |
176 | mode = &plane->state->crtc->state->mode; |
177 | fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal); |
178 | ability = clk_get_rate(clk: mixer->mod_clk); |
179 | /* BSP algorithm assumes 80% efficiency of VI scaler unit */ |
180 | ability *= 80; |
181 | do_div(ability, mode->vdisplay * fps * max(src_w, dst_w)); |
182 | |
183 | required = src_h * 100 / dst_h; |
184 | |
185 | if (ability < required) { |
186 | DRM_DEBUG_DRIVER("Using vertical coarse scaling\n" ); |
187 | vm = src_h; |
188 | vn = (u32)ability * dst_h / 100; |
189 | src_h = vn; |
190 | } |
191 | |
192 | /* it seems that every RGB scaler has buffer for 2048 pixels */ |
193 | scanline = subsampled ? mixer->cfg->scanline_yuv : 2048; |
194 | |
195 | if (src_w > scanline) { |
196 | DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n" ); |
197 | hm = src_w; |
198 | hn = scanline; |
199 | src_w = hn; |
200 | } |
201 | |
202 | hscale = (src_w << 16) / dst_w; |
203 | vscale = (src_h << 16) / dst_h; |
204 | |
205 | sun8i_vi_scaler_setup(mixer, layer: channel, src_w, src_h, dst_w, |
206 | dst_h, hscale, vscale, hphase, vphase, |
207 | format); |
208 | sun8i_vi_scaler_enable(mixer, layer: channel, enable: true); |
209 | } else { |
210 | DRM_DEBUG_DRIVER("HW scaling is not needed\n" ); |
211 | sun8i_vi_scaler_enable(mixer, layer: channel, enable: false); |
212 | } |
213 | |
214 | regmap_write(map: mixer->engine.regs, |
215 | SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base), |
216 | SUN8I_MIXER_CHAN_VI_DS_N(hn) | |
217 | SUN8I_MIXER_CHAN_VI_DS_M(hm)); |
218 | regmap_write(map: mixer->engine.regs, |
219 | SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base), |
220 | SUN8I_MIXER_CHAN_VI_DS_N(hn) | |
221 | SUN8I_MIXER_CHAN_VI_DS_M(hm)); |
222 | regmap_write(map: mixer->engine.regs, |
223 | SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base), |
224 | SUN8I_MIXER_CHAN_VI_DS_N(vn) | |
225 | SUN8I_MIXER_CHAN_VI_DS_M(vm)); |
226 | regmap_write(map: mixer->engine.regs, |
227 | SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base), |
228 | SUN8I_MIXER_CHAN_VI_DS_N(vn) | |
229 | SUN8I_MIXER_CHAN_VI_DS_M(vm)); |
230 | |
231 | /* Set base coordinates */ |
232 | DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n" , |
233 | state->dst.x1, state->dst.y1); |
234 | DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n" , dst_w, dst_h); |
235 | regmap_write(map: mixer->engine.regs, |
236 | SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), |
237 | SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); |
238 | regmap_write(map: mixer->engine.regs, |
239 | SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), |
240 | val: outsize); |
241 | |
242 | return 0; |
243 | } |
244 | |
245 | static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format) |
246 | { |
247 | if (!format->is_yuv) |
248 | return SUN8I_CSC_MODE_OFF; |
249 | |
250 | switch (format->format) { |
251 | case DRM_FORMAT_YVU411: |
252 | case DRM_FORMAT_YVU420: |
253 | case DRM_FORMAT_YVU422: |
254 | case DRM_FORMAT_YVU444: |
255 | return SUN8I_CSC_MODE_YVU2RGB; |
256 | default: |
257 | return SUN8I_CSC_MODE_YUV2RGB; |
258 | } |
259 | } |
260 | |
261 | static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, |
262 | int overlay, struct drm_plane *plane) |
263 | { |
264 | struct drm_plane_state *state = plane->state; |
265 | u32 val, ch_base, csc_mode, hw_fmt; |
266 | const struct drm_format_info *fmt; |
267 | int ret; |
268 | |
269 | ch_base = sun8i_channel_base(mixer, channel); |
270 | |
271 | fmt = state->fb->format; |
272 | ret = sun8i_mixer_drm_format_to_hw(format: fmt->format, hw_format: &hw_fmt); |
273 | if (ret) { |
274 | DRM_DEBUG_DRIVER("Invalid format\n" ); |
275 | return ret; |
276 | } |
277 | |
278 | val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; |
279 | regmap_update_bits(map: mixer->engine.regs, |
280 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), |
281 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); |
282 | |
283 | csc_mode = sun8i_vi_layer_get_csc_mode(format: fmt); |
284 | if (csc_mode != SUN8I_CSC_MODE_OFF) { |
285 | sun8i_csc_set_ccsc_coefficients(mixer, layer: channel, mode: csc_mode, |
286 | encoding: state->color_encoding, |
287 | range: state->color_range); |
288 | sun8i_csc_enable_ccsc(mixer, layer: channel, enable: true); |
289 | } else { |
290 | sun8i_csc_enable_ccsc(mixer, layer: channel, enable: false); |
291 | } |
292 | |
293 | if (!fmt->is_yuv) |
294 | val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; |
295 | else |
296 | val = 0; |
297 | |
298 | regmap_update_bits(map: mixer->engine.regs, |
299 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), |
300 | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, |
306 | int overlay, struct drm_plane *plane) |
307 | { |
308 | struct drm_plane_state *state = plane->state; |
309 | struct drm_framebuffer *fb = state->fb; |
310 | const struct drm_format_info *format = fb->format; |
311 | struct drm_gem_dma_object *gem; |
312 | u32 dx, dy, src_x, src_y; |
313 | dma_addr_t dma_addr; |
314 | u32 ch_base; |
315 | int i; |
316 | |
317 | ch_base = sun8i_channel_base(mixer, channel); |
318 | |
319 | /* Adjust x and y to be dividable by subsampling factor */ |
320 | src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); |
321 | src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); |
322 | |
323 | for (i = 0; i < format->num_planes; i++) { |
324 | /* Get the physical address of the buffer in memory */ |
325 | gem = drm_fb_dma_get_gem_obj(fb, plane: i); |
326 | |
327 | DRM_DEBUG_DRIVER("Using GEM @ %pad\n" , &gem->dma_addr); |
328 | |
329 | /* Compute the start of the displayed memory */ |
330 | dma_addr = gem->dma_addr + fb->offsets[i]; |
331 | |
332 | dx = src_x; |
333 | dy = src_y; |
334 | |
335 | if (i > 0) { |
336 | dx /= format->hsub; |
337 | dy /= format->vsub; |
338 | } |
339 | |
340 | /* Fixup framebuffer address for src coordinates */ |
341 | dma_addr += dx * format->cpp[i]; |
342 | dma_addr += dy * fb->pitches[i]; |
343 | |
344 | /* Set the line width */ |
345 | DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n" , |
346 | i + 1, fb->pitches[i]); |
347 | regmap_write(map: mixer->engine.regs, |
348 | SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base, |
349 | overlay, i), |
350 | val: fb->pitches[i]); |
351 | |
352 | DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n" , |
353 | i + 1, &dma_addr); |
354 | |
355 | regmap_write(map: mixer->engine.regs, |
356 | SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base, |
357 | overlay, i), |
358 | lower_32_bits(dma_addr)); |
359 | } |
360 | |
361 | return 0; |
362 | } |
363 | |
364 | static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, |
365 | struct drm_atomic_state *state) |
366 | { |
367 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
368 | plane); |
369 | struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); |
370 | struct drm_crtc *crtc = new_plane_state->crtc; |
371 | struct drm_crtc_state *crtc_state; |
372 | int min_scale, max_scale; |
373 | |
374 | if (!crtc) |
375 | return 0; |
376 | |
377 | crtc_state = drm_atomic_get_existing_crtc_state(state, |
378 | crtc); |
379 | if (WARN_ON(!crtc_state)) |
380 | return -EINVAL; |
381 | |
382 | min_scale = DRM_PLANE_NO_SCALING; |
383 | max_scale = DRM_PLANE_NO_SCALING; |
384 | |
385 | if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { |
386 | min_scale = SUN8I_VI_SCALER_SCALE_MIN; |
387 | max_scale = SUN8I_VI_SCALER_SCALE_MAX; |
388 | } |
389 | |
390 | return drm_atomic_helper_check_plane_state(plane_state: new_plane_state, |
391 | crtc_state, |
392 | min_scale, max_scale, |
393 | can_position: true, can_update_disabled: true); |
394 | } |
395 | |
396 | static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, |
397 | struct drm_atomic_state *state) |
398 | { |
399 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
400 | plane); |
401 | struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); |
402 | unsigned int old_zpos = old_state->normalized_zpos; |
403 | struct sun8i_mixer *mixer = layer->mixer; |
404 | |
405 | sun8i_vi_layer_enable(mixer, channel: layer->channel, overlay: layer->overlay, enable: false, zpos: 0, |
406 | old_zpos); |
407 | } |
408 | |
409 | static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, |
410 | struct drm_atomic_state *state) |
411 | { |
412 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
413 | plane); |
414 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
415 | plane); |
416 | struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); |
417 | unsigned int zpos = new_state->normalized_zpos; |
418 | unsigned int old_zpos = old_state->normalized_zpos; |
419 | struct sun8i_mixer *mixer = layer->mixer; |
420 | |
421 | if (!new_state->visible) { |
422 | sun8i_vi_layer_enable(mixer, channel: layer->channel, |
423 | overlay: layer->overlay, enable: false, zpos: 0, old_zpos); |
424 | return; |
425 | } |
426 | |
427 | sun8i_vi_layer_update_coord(mixer, channel: layer->channel, |
428 | overlay: layer->overlay, plane, zpos); |
429 | sun8i_vi_layer_update_alpha(mixer, channel: layer->channel, |
430 | overlay: layer->overlay, plane); |
431 | sun8i_vi_layer_update_formats(mixer, channel: layer->channel, |
432 | overlay: layer->overlay, plane); |
433 | sun8i_vi_layer_update_buffer(mixer, channel: layer->channel, |
434 | overlay: layer->overlay, plane); |
435 | sun8i_vi_layer_enable(mixer, channel: layer->channel, overlay: layer->overlay, |
436 | enable: true, zpos, old_zpos); |
437 | } |
438 | |
439 | static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { |
440 | .atomic_check = sun8i_vi_layer_atomic_check, |
441 | .atomic_disable = sun8i_vi_layer_atomic_disable, |
442 | .atomic_update = sun8i_vi_layer_atomic_update, |
443 | }; |
444 | |
445 | static const struct drm_plane_funcs sun8i_vi_layer_funcs = { |
446 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
447 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
448 | .destroy = drm_plane_cleanup, |
449 | .disable_plane = drm_atomic_helper_disable_plane, |
450 | .reset = drm_atomic_helper_plane_reset, |
451 | .update_plane = drm_atomic_helper_update_plane, |
452 | }; |
453 | |
454 | /* |
455 | * While DE2 VI layer supports same RGB formats as UI layer, alpha |
456 | * channel is ignored. This structure lists all unique variants |
457 | * where alpha channel is replaced with "don't care" (X) channel. |
458 | */ |
459 | static const u32 sun8i_vi_layer_formats[] = { |
460 | DRM_FORMAT_BGR565, |
461 | DRM_FORMAT_BGR888, |
462 | DRM_FORMAT_BGRX4444, |
463 | DRM_FORMAT_BGRX5551, |
464 | DRM_FORMAT_BGRX8888, |
465 | DRM_FORMAT_RGB565, |
466 | DRM_FORMAT_RGB888, |
467 | DRM_FORMAT_RGBX4444, |
468 | DRM_FORMAT_RGBX5551, |
469 | DRM_FORMAT_RGBX8888, |
470 | DRM_FORMAT_XBGR1555, |
471 | DRM_FORMAT_XBGR4444, |
472 | DRM_FORMAT_XBGR8888, |
473 | DRM_FORMAT_XRGB1555, |
474 | DRM_FORMAT_XRGB4444, |
475 | DRM_FORMAT_XRGB8888, |
476 | |
477 | DRM_FORMAT_NV16, |
478 | DRM_FORMAT_NV12, |
479 | DRM_FORMAT_NV21, |
480 | DRM_FORMAT_NV61, |
481 | DRM_FORMAT_UYVY, |
482 | DRM_FORMAT_VYUY, |
483 | DRM_FORMAT_YUYV, |
484 | DRM_FORMAT_YVYU, |
485 | DRM_FORMAT_YUV411, |
486 | DRM_FORMAT_YUV420, |
487 | DRM_FORMAT_YUV422, |
488 | DRM_FORMAT_YVU411, |
489 | DRM_FORMAT_YVU420, |
490 | DRM_FORMAT_YVU422, |
491 | }; |
492 | |
493 | static const u32 sun8i_vi_layer_de3_formats[] = { |
494 | DRM_FORMAT_ABGR1555, |
495 | DRM_FORMAT_ABGR2101010, |
496 | DRM_FORMAT_ABGR4444, |
497 | DRM_FORMAT_ABGR8888, |
498 | DRM_FORMAT_ARGB1555, |
499 | DRM_FORMAT_ARGB2101010, |
500 | DRM_FORMAT_ARGB4444, |
501 | DRM_FORMAT_ARGB8888, |
502 | DRM_FORMAT_BGR565, |
503 | DRM_FORMAT_BGR888, |
504 | DRM_FORMAT_BGRA1010102, |
505 | DRM_FORMAT_BGRA5551, |
506 | DRM_FORMAT_BGRA4444, |
507 | DRM_FORMAT_BGRA8888, |
508 | DRM_FORMAT_BGRX8888, |
509 | DRM_FORMAT_RGB565, |
510 | DRM_FORMAT_RGB888, |
511 | DRM_FORMAT_RGBA1010102, |
512 | DRM_FORMAT_RGBA4444, |
513 | DRM_FORMAT_RGBA5551, |
514 | DRM_FORMAT_RGBA8888, |
515 | DRM_FORMAT_RGBX8888, |
516 | DRM_FORMAT_XBGR8888, |
517 | DRM_FORMAT_XRGB8888, |
518 | |
519 | DRM_FORMAT_NV16, |
520 | DRM_FORMAT_NV12, |
521 | DRM_FORMAT_NV21, |
522 | DRM_FORMAT_NV61, |
523 | DRM_FORMAT_P010, |
524 | DRM_FORMAT_P210, |
525 | DRM_FORMAT_UYVY, |
526 | DRM_FORMAT_VYUY, |
527 | DRM_FORMAT_YUYV, |
528 | DRM_FORMAT_YVYU, |
529 | DRM_FORMAT_YUV411, |
530 | DRM_FORMAT_YUV420, |
531 | DRM_FORMAT_YUV422, |
532 | DRM_FORMAT_YVU411, |
533 | DRM_FORMAT_YVU420, |
534 | DRM_FORMAT_YVU422, |
535 | }; |
536 | |
537 | static const uint64_t sun8i_layer_modifiers[] = { |
538 | DRM_FORMAT_MOD_LINEAR, |
539 | DRM_FORMAT_MOD_INVALID |
540 | }; |
541 | |
542 | struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, |
543 | struct sun8i_mixer *mixer, |
544 | int index) |
545 | { |
546 | enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; |
547 | u32 supported_encodings, supported_ranges; |
548 | unsigned int plane_cnt, format_count; |
549 | struct sun8i_vi_layer *layer; |
550 | const u32 *formats; |
551 | int ret; |
552 | |
553 | layer = devm_kzalloc(dev: drm->dev, size: sizeof(*layer), GFP_KERNEL); |
554 | if (!layer) |
555 | return ERR_PTR(error: -ENOMEM); |
556 | |
557 | if (mixer->cfg->is_de3) { |
558 | formats = sun8i_vi_layer_de3_formats; |
559 | format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); |
560 | } else { |
561 | formats = sun8i_vi_layer_formats; |
562 | format_count = ARRAY_SIZE(sun8i_vi_layer_formats); |
563 | } |
564 | |
565 | if (!mixer->cfg->ui_num && index == 0) |
566 | type = DRM_PLANE_TYPE_PRIMARY; |
567 | |
568 | /* possible crtcs are set later */ |
569 | ret = drm_universal_plane_init(dev: drm, plane: &layer->plane, possible_crtcs: 0, |
570 | funcs: &sun8i_vi_layer_funcs, |
571 | formats, format_count, |
572 | format_modifiers: sun8i_layer_modifiers, |
573 | type, NULL); |
574 | if (ret) { |
575 | dev_err(drm->dev, "Couldn't initialize layer\n" ); |
576 | return ERR_PTR(error: ret); |
577 | } |
578 | |
579 | plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; |
580 | |
581 | if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) { |
582 | ret = drm_plane_create_alpha_property(plane: &layer->plane); |
583 | if (ret) { |
584 | dev_err(drm->dev, "Couldn't add alpha property\n" ); |
585 | return ERR_PTR(error: ret); |
586 | } |
587 | } |
588 | |
589 | ret = drm_plane_create_zpos_property(plane: &layer->plane, zpos: index, |
590 | min: 0, max: plane_cnt - 1); |
591 | if (ret) { |
592 | dev_err(drm->dev, "Couldn't add zpos property\n" ); |
593 | return ERR_PTR(error: ret); |
594 | } |
595 | |
596 | supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) | |
597 | BIT(DRM_COLOR_YCBCR_BT709); |
598 | if (mixer->cfg->is_de3) |
599 | supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020); |
600 | |
601 | supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | |
602 | BIT(DRM_COLOR_YCBCR_FULL_RANGE); |
603 | |
604 | ret = drm_plane_create_color_properties(plane: &layer->plane, |
605 | supported_encodings, |
606 | supported_ranges, |
607 | default_encoding: DRM_COLOR_YCBCR_BT709, |
608 | default_range: DRM_COLOR_YCBCR_LIMITED_RANGE); |
609 | if (ret) { |
610 | dev_err(drm->dev, "Couldn't add encoding and range properties!\n" ); |
611 | return ERR_PTR(error: ret); |
612 | } |
613 | |
614 | drm_plane_helper_add(plane: &layer->plane, funcs: &sun8i_vi_layer_helper_funcs); |
615 | layer->mixer = mixer; |
616 | layer->channel = index; |
617 | layer->overlay = 0; |
618 | |
619 | return layer; |
620 | } |
621 | |