1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
6 #include <drm/drm_atomic.h>
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_crtc.h>
9 #include <drm/drm_fb_cma_helper.h>
10 #include <drm/drm_gem_cma_helper.h>
11 #include <drm/drm_gem_framebuffer_helper.h>
12 #include <drm/drm_plane_helper.h>
13 #include <drm/drm_probe_helper.h>
15 #include "sun8i_csc.h"
16 #include "sun8i_mixer.h"
17 #include "sun8i_vi_layer.h"
18 #include "sun8i_vi_scaler.h"
20 static void sun8i_vi_layer_enable(struct sun8i_mixer
*mixer
, int channel
,
21 int overlay
, bool enable
, unsigned int zpos
,
22 unsigned int old_zpos
)
24 u32 val
, bld_base
, ch_base
;
26 bld_base
= sun8i_blender_base(mixer
);
27 ch_base
= sun8i_channel_base(mixer
, channel
);
29 DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
30 enable
? "En" : "Dis", channel
, overlay
);
33 val
= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN
;
37 regmap_update_bits(mixer
->engine
.regs
,
38 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base
, overlay
),
39 SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN
, val
);
41 if (!enable
|| zpos
!= old_zpos
) {
42 regmap_update_bits(mixer
->engine
.regs
,
43 SUN8I_MIXER_BLEND_PIPE_CTL(bld_base
),
44 SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos
),
47 regmap_update_bits(mixer
->engine
.regs
,
48 SUN8I_MIXER_BLEND_ROUTE(bld_base
),
49 SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos
),
54 val
= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos
);
56 regmap_update_bits(mixer
->engine
.regs
,
57 SUN8I_MIXER_BLEND_PIPE_CTL(bld_base
),
60 val
= channel
<< SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos
);
62 regmap_update_bits(mixer
->engine
.regs
,
63 SUN8I_MIXER_BLEND_ROUTE(bld_base
),
64 SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos
),
69 static int sun8i_vi_layer_update_coord(struct sun8i_mixer
*mixer
, int channel
,
70 int overlay
, struct drm_plane
*plane
,
73 struct drm_plane_state
*state
= plane
->state
;
74 const struct drm_format_info
*format
= state
->fb
->format
;
75 u32 src_w
, src_h
, dst_w
, dst_h
;
76 u32 bld_base
, ch_base
;
83 DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
86 bld_base
= sun8i_blender_base(mixer
);
87 ch_base
= sun8i_channel_base(mixer
, channel
);
89 src_w
= drm_rect_width(&state
->src
) >> 16;
90 src_h
= drm_rect_height(&state
->src
) >> 16;
91 dst_w
= drm_rect_width(&state
->dst
);
92 dst_h
= drm_rect_height(&state
->dst
);
94 hphase
= state
->src
.x1
& 0xffff;
95 vphase
= state
->src
.y1
& 0xffff;
97 /* make coordinates dividable by subsampling factor */
98 if (format
->hsub
> 1) {
101 mask
= format
->hsub
- 1;
102 remainder
= (state
->src
.x1
>> 16) & mask
;
103 src_w
= (src_w
+ remainder
) & ~mask
;
104 hphase
+= remainder
<< 16;
107 if (format
->vsub
> 1) {
110 mask
= format
->vsub
- 1;
111 remainder
= (state
->src
.y1
>> 16) & mask
;
112 src_h
= (src_h
+ remainder
) & ~mask
;
113 vphase
+= remainder
<< 16;
116 insize
= SUN8I_MIXER_SIZE(src_w
, src_h
);
117 outsize
= SUN8I_MIXER_SIZE(dst_w
, dst_h
);
119 /* Set height and width */
120 DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
121 (state
->src
.x1
>> 16) & ~(format
->hsub
- 1),
122 (state
->src
.y1
>> 16) & ~(format
->vsub
- 1));
123 DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w
, src_h
);
124 regmap_write(mixer
->engine
.regs
,
125 SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base
, overlay
),
127 regmap_write(mixer
->engine
.regs
,
128 SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base
),
132 * Scaler must be enabled for subsampled formats, so it scales
133 * chroma to same size as luma.
135 subsampled
= format
->hsub
> 1 || format
->vsub
> 1;
137 if (insize
!= outsize
|| subsampled
|| hphase
|| vphase
) {
138 unsigned int scanline
, required
;
139 struct drm_display_mode
*mode
;
140 u32 hscale
, vscale
, fps
;
143 DRM_DEBUG_DRIVER("HW scaling is enabled\n");
145 mode
= &plane
->state
->crtc
->state
->mode
;
146 fps
= (mode
->clock
* 1000) / (mode
->vtotal
* mode
->htotal
);
147 ability
= clk_get_rate(mixer
->mod_clk
);
148 /* BSP algorithm assumes 80% efficiency of VI scaler unit */
150 do_div(ability
, mode
->vdisplay
* fps
* max(src_w
, dst_w
));
152 required
= src_h
* 100 / dst_h
;
154 if (ability
< required
) {
155 DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
157 vn
= (u32
)ability
* dst_h
/ 100;
161 /* it seems that every RGB scaler has buffer for 2048 pixels */
162 scanline
= subsampled
? mixer
->cfg
->scanline_yuv
: 2048;
164 if (src_w
> scanline
) {
165 DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
171 hscale
= (src_w
<< 16) / dst_w
;
172 vscale
= (src_h
<< 16) / dst_h
;
174 sun8i_vi_scaler_setup(mixer
, channel
, src_w
, src_h
, dst_w
,
175 dst_h
, hscale
, vscale
, hphase
, vphase
,
177 sun8i_vi_scaler_enable(mixer
, channel
, true);
179 DRM_DEBUG_DRIVER("HW scaling is not needed\n");
180 sun8i_vi_scaler_enable(mixer
, channel
, false);
183 regmap_write(mixer
->engine
.regs
,
184 SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base
),
185 SUN8I_MIXER_CHAN_VI_DS_N(hn
) |
186 SUN8I_MIXER_CHAN_VI_DS_M(hm
));
187 regmap_write(mixer
->engine
.regs
,
188 SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base
),
189 SUN8I_MIXER_CHAN_VI_DS_N(hn
) |
190 SUN8I_MIXER_CHAN_VI_DS_M(hm
));
191 regmap_write(mixer
->engine
.regs
,
192 SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base
),
193 SUN8I_MIXER_CHAN_VI_DS_N(vn
) |
194 SUN8I_MIXER_CHAN_VI_DS_M(vm
));
195 regmap_write(mixer
->engine
.regs
,
196 SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base
),
197 SUN8I_MIXER_CHAN_VI_DS_N(vn
) |
198 SUN8I_MIXER_CHAN_VI_DS_M(vm
));
200 /* Set base coordinates */
201 DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
202 state
->dst
.x1
, state
->dst
.y1
);
203 DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w
, dst_h
);
204 regmap_write(mixer
->engine
.regs
,
205 SUN8I_MIXER_BLEND_ATTR_COORD(bld_base
, zpos
),
206 SUN8I_MIXER_COORD(state
->dst
.x1
, state
->dst
.y1
));
207 regmap_write(mixer
->engine
.regs
,
208 SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base
, zpos
),
214 static u32
sun8i_vi_layer_get_csc_mode(const struct drm_format_info
*format
)
217 return SUN8I_CSC_MODE_OFF
;
219 switch (format
->format
) {
220 case DRM_FORMAT_YVU411
:
221 case DRM_FORMAT_YVU420
:
222 case DRM_FORMAT_YVU422
:
223 case DRM_FORMAT_YVU444
:
224 return SUN8I_CSC_MODE_YVU2RGB
;
226 return SUN8I_CSC_MODE_YUV2RGB
;
230 static int sun8i_vi_layer_update_formats(struct sun8i_mixer
*mixer
, int channel
,
231 int overlay
, struct drm_plane
*plane
)
233 struct drm_plane_state
*state
= plane
->state
;
234 u32 val
, ch_base
, csc_mode
, hw_fmt
;
235 const struct drm_format_info
*fmt
;
238 ch_base
= sun8i_channel_base(mixer
, channel
);
240 fmt
= state
->fb
->format
;
241 ret
= sun8i_mixer_drm_format_to_hw(fmt
->format
, &hw_fmt
);
243 DRM_DEBUG_DRIVER("Invalid format\n");
247 val
= hw_fmt
<< SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET
;
248 regmap_update_bits(mixer
->engine
.regs
,
249 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base
, overlay
),
250 SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK
, val
);
252 csc_mode
= sun8i_vi_layer_get_csc_mode(fmt
);
253 if (csc_mode
!= SUN8I_CSC_MODE_OFF
) {
254 sun8i_csc_set_ccsc_coefficients(mixer
, channel
, csc_mode
,
255 state
->color_encoding
,
257 sun8i_csc_enable_ccsc(mixer
, channel
, true);
259 sun8i_csc_enable_ccsc(mixer
, channel
, false);
263 val
= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE
;
267 regmap_update_bits(mixer
->engine
.regs
,
268 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base
, overlay
),
269 SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE
, val
);
271 /* It seems that YUV formats use global alpha setting. */
272 if (mixer
->cfg
->is_de3
)
273 regmap_update_bits(mixer
->engine
.regs
,
274 SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base
,
276 SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK
,
277 SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(0xff));
282 static int sun8i_vi_layer_update_buffer(struct sun8i_mixer
*mixer
, int channel
,
283 int overlay
, struct drm_plane
*plane
)
285 struct drm_plane_state
*state
= plane
->state
;
286 struct drm_framebuffer
*fb
= state
->fb
;
287 const struct drm_format_info
*format
= fb
->format
;
288 struct drm_gem_cma_object
*gem
;
289 u32 dx
, dy
, src_x
, src_y
;
294 ch_base
= sun8i_channel_base(mixer
, channel
);
296 /* Adjust x and y to be dividable by subsampling factor */
297 src_x
= (state
->src
.x1
>> 16) & ~(format
->hsub
- 1);
298 src_y
= (state
->src
.y1
>> 16) & ~(format
->vsub
- 1);
300 for (i
= 0; i
< format
->num_planes
; i
++) {
301 /* Get the physical address of the buffer in memory */
302 gem
= drm_fb_cma_get_gem_obj(fb
, i
);
304 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem
->paddr
);
306 /* Compute the start of the displayed memory */
307 paddr
= gem
->paddr
+ fb
->offsets
[i
];
317 /* Fixup framebuffer address for src coordinates */
318 paddr
+= dx
* format
->cpp
[i
];
319 paddr
+= dy
* fb
->pitches
[i
];
321 /* Set the line width */
322 DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
323 i
+ 1, fb
->pitches
[i
]);
324 regmap_write(mixer
->engine
.regs
,
325 SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base
,
329 DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
332 regmap_write(mixer
->engine
.regs
,
333 SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base
,
335 lower_32_bits(paddr
));
341 static int sun8i_vi_layer_atomic_check(struct drm_plane
*plane
,
342 struct drm_plane_state
*state
)
344 struct sun8i_vi_layer
*layer
= plane_to_sun8i_vi_layer(plane
);
345 struct drm_crtc
*crtc
= state
->crtc
;
346 struct drm_crtc_state
*crtc_state
;
347 int min_scale
, max_scale
;
352 crtc_state
= drm_atomic_get_existing_crtc_state(state
->state
, crtc
);
353 if (WARN_ON(!crtc_state
))
356 min_scale
= DRM_PLANE_HELPER_NO_SCALING
;
357 max_scale
= DRM_PLANE_HELPER_NO_SCALING
;
359 if (layer
->mixer
->cfg
->scaler_mask
& BIT(layer
->channel
)) {
360 min_scale
= SUN8I_VI_SCALER_SCALE_MIN
;
361 max_scale
= SUN8I_VI_SCALER_SCALE_MAX
;
364 return drm_atomic_helper_check_plane_state(state
, crtc_state
,
365 min_scale
, max_scale
,
369 static void sun8i_vi_layer_atomic_disable(struct drm_plane
*plane
,
370 struct drm_plane_state
*old_state
)
372 struct sun8i_vi_layer
*layer
= plane_to_sun8i_vi_layer(plane
);
373 unsigned int old_zpos
= old_state
->normalized_zpos
;
374 struct sun8i_mixer
*mixer
= layer
->mixer
;
376 sun8i_vi_layer_enable(mixer
, layer
->channel
, layer
->overlay
, false, 0,
380 static void sun8i_vi_layer_atomic_update(struct drm_plane
*plane
,
381 struct drm_plane_state
*old_state
)
383 struct sun8i_vi_layer
*layer
= plane_to_sun8i_vi_layer(plane
);
384 unsigned int zpos
= plane
->state
->normalized_zpos
;
385 unsigned int old_zpos
= old_state
->normalized_zpos
;
386 struct sun8i_mixer
*mixer
= layer
->mixer
;
388 if (!plane
->state
->visible
) {
389 sun8i_vi_layer_enable(mixer
, layer
->channel
,
390 layer
->overlay
, false, 0, old_zpos
);
394 sun8i_vi_layer_update_coord(mixer
, layer
->channel
,
395 layer
->overlay
, plane
, zpos
);
396 sun8i_vi_layer_update_formats(mixer
, layer
->channel
,
397 layer
->overlay
, plane
);
398 sun8i_vi_layer_update_buffer(mixer
, layer
->channel
,
399 layer
->overlay
, plane
);
400 sun8i_vi_layer_enable(mixer
, layer
->channel
, layer
->overlay
,
401 true, zpos
, old_zpos
);
404 static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs
= {
405 .prepare_fb
= drm_gem_fb_prepare_fb
,
406 .atomic_check
= sun8i_vi_layer_atomic_check
,
407 .atomic_disable
= sun8i_vi_layer_atomic_disable
,
408 .atomic_update
= sun8i_vi_layer_atomic_update
,
411 static const struct drm_plane_funcs sun8i_vi_layer_funcs
= {
412 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
413 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
414 .destroy
= drm_plane_cleanup
,
415 .disable_plane
= drm_atomic_helper_disable_plane
,
416 .reset
= drm_atomic_helper_plane_reset
,
417 .update_plane
= drm_atomic_helper_update_plane
,
421 * While DE2 VI layer supports same RGB formats as UI layer, alpha
422 * channel is ignored. This structure lists all unique variants
423 * where alpha channel is replaced with "don't care" (X) channel.
425 static const u32 sun8i_vi_layer_formats
[] = {
459 static const u32 sun8i_vi_layer_de3_formats
[] = {
461 DRM_FORMAT_ABGR2101010
,
465 DRM_FORMAT_ARGB2101010
,
470 DRM_FORMAT_BGRA1010102
,
477 DRM_FORMAT_RGBA1010102
,
503 struct sun8i_vi_layer
*sun8i_vi_layer_init_one(struct drm_device
*drm
,
504 struct sun8i_mixer
*mixer
,
507 u32 supported_encodings
, supported_ranges
;
508 unsigned int plane_cnt
, format_count
;
509 struct sun8i_vi_layer
*layer
;
513 layer
= devm_kzalloc(drm
->dev
, sizeof(*layer
), GFP_KERNEL
);
515 return ERR_PTR(-ENOMEM
);
517 if (mixer
->cfg
->is_de3
) {
518 formats
= sun8i_vi_layer_de3_formats
;
519 format_count
= ARRAY_SIZE(sun8i_vi_layer_de3_formats
);
521 formats
= sun8i_vi_layer_formats
;
522 format_count
= ARRAY_SIZE(sun8i_vi_layer_formats
);
525 /* possible crtcs are set later */
526 ret
= drm_universal_plane_init(drm
, &layer
->plane
, 0,
527 &sun8i_vi_layer_funcs
,
528 formats
, format_count
,
529 NULL
, DRM_PLANE_TYPE_OVERLAY
, NULL
);
531 dev_err(drm
->dev
, "Couldn't initialize layer\n");
535 plane_cnt
= mixer
->cfg
->ui_num
+ mixer
->cfg
->vi_num
;
537 ret
= drm_plane_create_zpos_property(&layer
->plane
, index
,
540 dev_err(drm
->dev
, "Couldn't add zpos property\n");
544 supported_encodings
= BIT(DRM_COLOR_YCBCR_BT601
) |
545 BIT(DRM_COLOR_YCBCR_BT709
);
547 supported_ranges
= BIT(DRM_COLOR_YCBCR_LIMITED_RANGE
) |
548 BIT(DRM_COLOR_YCBCR_FULL_RANGE
);
550 ret
= drm_plane_create_color_properties(&layer
->plane
,
553 DRM_COLOR_YCBCR_BT709
,
554 DRM_COLOR_YCBCR_LIMITED_RANGE
);
556 dev_err(drm
->dev
, "Couldn't add encoding and range properties!\n");
560 drm_plane_helper_add(&layer
->plane
, &sun8i_vi_layer_helper_funcs
);
561 layer
->mixer
= mixer
;
562 layer
->channel
= index
;