12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27480ba4dSJernej Skrabec /*
37480ba4dSJernej Skrabec * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
47480ba4dSJernej Skrabec */
57480ba4dSJernej Skrabec
67480ba4dSJernej Skrabec #include <drm/drm_atomic.h>
77480ba4dSJernej Skrabec #include <drm/drm_atomic_helper.h>
890bb087fSVille Syrjälä #include <drm/drm_blend.h>
97480ba4dSJernej Skrabec #include <drm/drm_crtc.h>
106bcfe8eaSDanilo Krummrich #include <drm/drm_fb_dma_helper.h>
11720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
12820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
134a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
14fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
157480ba4dSJernej Skrabec
16daab3d0eSJernej Skrabec #include "sun8i_csc.h"
177480ba4dSJernej Skrabec #include "sun8i_mixer.h"
181ef1380cSJernej Skrabec #include "sun8i_vi_layer.h"
19b862a648SJernej Skrabec #include "sun8i_vi_scaler.h"
207480ba4dSJernej Skrabec
sun8i_vi_layer_enable(struct sun8i_mixer * mixer,int channel,int overlay,bool enable,unsigned int zpos,unsigned int old_zpos)217480ba4dSJernej Skrabec static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
22d8b3f454SPaul Kocialkowski int overlay, bool enable, unsigned int zpos,
23d8b3f454SPaul Kocialkowski unsigned int old_zpos)
247480ba4dSJernej Skrabec {
254b09c073SJernej Skrabec u32 val, bld_base, ch_base;
264b09c073SJernej Skrabec
274b09c073SJernej Skrabec bld_base = sun8i_blender_base(mixer);
284b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel);
297480ba4dSJernej Skrabec
307480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
317480ba4dSJernej Skrabec enable ? "En" : "Dis", channel, overlay);
327480ba4dSJernej Skrabec
337480ba4dSJernej Skrabec if (enable)
347480ba4dSJernej Skrabec val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
357480ba4dSJernej Skrabec else
367480ba4dSJernej Skrabec val = 0;
377480ba4dSJernej Skrabec
387480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs,
394b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
407480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
417480ba4dSJernej Skrabec
42d8b3f454SPaul Kocialkowski if (!enable || zpos != old_zpos) {
43d8b3f454SPaul Kocialkowski regmap_update_bits(mixer->engine.regs,
444b09c073SJernej Skrabec SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
45d8b3f454SPaul Kocialkowski SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
46d8b3f454SPaul Kocialkowski 0);
47d8b3f454SPaul Kocialkowski
48d8b3f454SPaul Kocialkowski regmap_update_bits(mixer->engine.regs,
494b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ROUTE(bld_base),
50d8b3f454SPaul Kocialkowski SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
51d8b3f454SPaul Kocialkowski 0);
52d8b3f454SPaul Kocialkowski }
53d8b3f454SPaul Kocialkowski
54f88c5ee7SJernej Skrabec if (enable) {
55f88c5ee7SJernej Skrabec val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
567480ba4dSJernej Skrabec
577480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs,
584b09c073SJernej Skrabec SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
594b09c073SJernej Skrabec val, val);
60f88c5ee7SJernej Skrabec
61f88c5ee7SJernej Skrabec val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
62f88c5ee7SJernej Skrabec
63f88c5ee7SJernej Skrabec regmap_update_bits(mixer->engine.regs,
644b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ROUTE(bld_base),
65f88c5ee7SJernej Skrabec SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
66f88c5ee7SJernej Skrabec val);
67f88c5ee7SJernej Skrabec }
687480ba4dSJernej Skrabec }
697480ba4dSJernej Skrabec
sun8i_vi_layer_update_alpha(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane)70c0f6f0c4SRoman Stratiienko static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
71c0f6f0c4SRoman Stratiienko int overlay, struct drm_plane *plane)
72c0f6f0c4SRoman Stratiienko {
73c0f6f0c4SRoman Stratiienko u32 mask, val, ch_base;
74c0f6f0c4SRoman Stratiienko
75c0f6f0c4SRoman Stratiienko ch_base = sun8i_channel_base(mixer, channel);
76c0f6f0c4SRoman Stratiienko
77c0f6f0c4SRoman Stratiienko if (mixer->cfg->is_de3) {
78c0f6f0c4SRoman Stratiienko mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
79c0f6f0c4SRoman Stratiienko SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
80c0f6f0c4SRoman Stratiienko val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
81c0f6f0c4SRoman Stratiienko (plane->state->alpha >> 8);
82c0f6f0c4SRoman Stratiienko
83c0f6f0c4SRoman Stratiienko val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
84c0f6f0c4SRoman Stratiienko SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
85c0f6f0c4SRoman Stratiienko SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
86c0f6f0c4SRoman Stratiienko
87c0f6f0c4SRoman Stratiienko regmap_update_bits(mixer->engine.regs,
88c0f6f0c4SRoman Stratiienko SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
89c0f6f0c4SRoman Stratiienko overlay),
90c0f6f0c4SRoman Stratiienko mask, val);
91c0f6f0c4SRoman Stratiienko } else if (mixer->cfg->vi_num == 1) {
92c0f6f0c4SRoman Stratiienko regmap_update_bits(mixer->engine.regs,
93c0f6f0c4SRoman Stratiienko SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
94c0f6f0c4SRoman Stratiienko SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
95c0f6f0c4SRoman Stratiienko SUN8I_MIXER_FCC_GLOBAL_ALPHA
96c0f6f0c4SRoman Stratiienko (plane->state->alpha >> 8));
97c0f6f0c4SRoman Stratiienko }
98c0f6f0c4SRoman Stratiienko }
99c0f6f0c4SRoman Stratiienko
sun8i_vi_layer_update_coord(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane,unsigned int zpos)1007480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
101f88c5ee7SJernej Skrabec int overlay, struct drm_plane *plane,
102f88c5ee7SJernej Skrabec unsigned int zpos)
1037480ba4dSJernej Skrabec {
1047480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state;
1051343bd6cSJernej Skrabec const struct drm_format_info *format = state->fb->format;
106b862a648SJernej Skrabec u32 src_w, src_h, dst_w, dst_h;
1074b09c073SJernej Skrabec u32 bld_base, ch_base;
108b862a648SJernej Skrabec u32 outsize, insize;
109b862a648SJernej Skrabec u32 hphase, vphase;
110a7db690cSJernej Skrabec u32 hn = 0, hm = 0;
111a7db690cSJernej Skrabec u32 vn = 0, vm = 0;
112e1ef9006SJernej Skrabec bool subsampled;
1137480ba4dSJernej Skrabec
1147480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
1157480ba4dSJernej Skrabec channel, overlay);
116b862a648SJernej Skrabec
1174b09c073SJernej Skrabec bld_base = sun8i_blender_base(mixer);
1184b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel);
1194b09c073SJernej Skrabec
120b862a648SJernej Skrabec src_w = drm_rect_width(&state->src) >> 16;
121b862a648SJernej Skrabec src_h = drm_rect_height(&state->src) >> 16;
122b862a648SJernej Skrabec dst_w = drm_rect_width(&state->dst);
123b862a648SJernej Skrabec dst_h = drm_rect_height(&state->dst);
124b862a648SJernej Skrabec
125b862a648SJernej Skrabec hphase = state->src.x1 & 0xffff;
126b862a648SJernej Skrabec vphase = state->src.y1 & 0xffff;
127b862a648SJernej Skrabec
128e1ef9006SJernej Skrabec /* make coordinates dividable by subsampling factor */
129e1ef9006SJernej Skrabec if (format->hsub > 1) {
130e1ef9006SJernej Skrabec int mask, remainder;
131e1ef9006SJernej Skrabec
132e1ef9006SJernej Skrabec mask = format->hsub - 1;
133e1ef9006SJernej Skrabec remainder = (state->src.x1 >> 16) & mask;
134e1ef9006SJernej Skrabec src_w = (src_w + remainder) & ~mask;
135e1ef9006SJernej Skrabec hphase += remainder << 16;
136e1ef9006SJernej Skrabec }
137e1ef9006SJernej Skrabec
138e1ef9006SJernej Skrabec if (format->vsub > 1) {
139e1ef9006SJernej Skrabec int mask, remainder;
140e1ef9006SJernej Skrabec
141e1ef9006SJernej Skrabec mask = format->vsub - 1;
142e1ef9006SJernej Skrabec remainder = (state->src.y1 >> 16) & mask;
143e1ef9006SJernej Skrabec src_h = (src_h + remainder) & ~mask;
144e1ef9006SJernej Skrabec vphase += remainder << 16;
145e1ef9006SJernej Skrabec }
146e1ef9006SJernej Skrabec
147b862a648SJernej Skrabec insize = SUN8I_MIXER_SIZE(src_w, src_h);
148b862a648SJernej Skrabec outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
1497480ba4dSJernej Skrabec
1507480ba4dSJernej Skrabec /* Set height and width */
151b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
152e1ef9006SJernej Skrabec (state->src.x1 >> 16) & ~(format->hsub - 1),
153e1ef9006SJernej Skrabec (state->src.y1 >> 16) & ~(format->vsub - 1));
154b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
1557480ba4dSJernej Skrabec regmap_write(mixer->engine.regs,
1564b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
157b862a648SJernej Skrabec insize);
1587480ba4dSJernej Skrabec regmap_write(mixer->engine.regs,
1594b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
160b862a648SJernej Skrabec insize);
161b862a648SJernej Skrabec
162e1ef9006SJernej Skrabec /*
163e1ef9006SJernej Skrabec * Scaler must be enabled for subsampled formats, so it scales
164e1ef9006SJernej Skrabec * chroma to same size as luma.
165e1ef9006SJernej Skrabec */
166e1ef9006SJernej Skrabec subsampled = format->hsub > 1 || format->vsub > 1;
167e1ef9006SJernej Skrabec
168e1ef9006SJernej Skrabec if (insize != outsize || subsampled || hphase || vphase) {
169a7db690cSJernej Skrabec unsigned int scanline, required;
170a7db690cSJernej Skrabec struct drm_display_mode *mode;
171a7db690cSJernej Skrabec u32 hscale, vscale, fps;
172a7db690cSJernej Skrabec u64 ability;
173b862a648SJernej Skrabec
174b862a648SJernej Skrabec DRM_DEBUG_DRIVER("HW scaling is enabled\n");
175b862a648SJernej Skrabec
176a7db690cSJernej Skrabec mode = &plane->state->crtc->state->mode;
177a7db690cSJernej Skrabec fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
178a7db690cSJernej Skrabec ability = clk_get_rate(mixer->mod_clk);
179a7db690cSJernej Skrabec /* BSP algorithm assumes 80% efficiency of VI scaler unit */
180a7db690cSJernej Skrabec ability *= 80;
181a7db690cSJernej Skrabec do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
182a7db690cSJernej Skrabec
183a7db690cSJernej Skrabec required = src_h * 100 / dst_h;
184a7db690cSJernej Skrabec
185a7db690cSJernej Skrabec if (ability < required) {
186a7db690cSJernej Skrabec DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
187a7db690cSJernej Skrabec vm = src_h;
188a7db690cSJernej Skrabec vn = (u32)ability * dst_h / 100;
189a7db690cSJernej Skrabec src_h = vn;
190a7db690cSJernej Skrabec }
191a7db690cSJernej Skrabec
192a7db690cSJernej Skrabec /* it seems that every RGB scaler has buffer for 2048 pixels */
193a7db690cSJernej Skrabec scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
194a7db690cSJernej Skrabec
195a7db690cSJernej Skrabec if (src_w > scanline) {
196a7db690cSJernej Skrabec DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
197a7db690cSJernej Skrabec hm = src_w;
198a7db690cSJernej Skrabec hn = scanline;
199a7db690cSJernej Skrabec src_w = hn;
200a7db690cSJernej Skrabec }
201a7db690cSJernej Skrabec
202a7db690cSJernej Skrabec hscale = (src_w << 16) / dst_w;
203a7db690cSJernej Skrabec vscale = (src_h << 16) / dst_h;
204b862a648SJernej Skrabec
205b862a648SJernej Skrabec sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
2061343bd6cSJernej Skrabec dst_h, hscale, vscale, hphase, vphase,
2071343bd6cSJernej Skrabec format);
208b862a648SJernej Skrabec sun8i_vi_scaler_enable(mixer, channel, true);
209b862a648SJernej Skrabec } else {
210b862a648SJernej Skrabec DRM_DEBUG_DRIVER("HW scaling is not needed\n");
211b862a648SJernej Skrabec sun8i_vi_scaler_enable(mixer, channel, false);
212b862a648SJernej Skrabec }
2137480ba4dSJernej Skrabec
214a7db690cSJernej Skrabec regmap_write(mixer->engine.regs,
215a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
216a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_N(hn) |
217a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_M(hm));
218a7db690cSJernej Skrabec regmap_write(mixer->engine.regs,
219a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
220a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_N(hn) |
221a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_M(hm));
222a7db690cSJernej Skrabec regmap_write(mixer->engine.regs,
223a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
224a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_N(vn) |
225a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_M(vm));
226a7db690cSJernej Skrabec regmap_write(mixer->engine.regs,
227a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
228a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_N(vn) |
229a7db690cSJernej Skrabec SUN8I_MIXER_CHAN_VI_DS_M(vm));
230a7db690cSJernej Skrabec
2317480ba4dSJernej Skrabec /* Set base coordinates */
232b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
2337480ba4dSJernej Skrabec state->dst.x1, state->dst.y1);
234b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
2357480ba4dSJernej Skrabec regmap_write(mixer->engine.regs,
2364b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
2377480ba4dSJernej Skrabec SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
2387480ba4dSJernej Skrabec regmap_write(mixer->engine.regs,
2394b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
240b862a648SJernej Skrabec outsize);
2417480ba4dSJernej Skrabec
2427480ba4dSJernej Skrabec return 0;
2437480ba4dSJernej Skrabec }
2447480ba4dSJernej Skrabec
sun8i_vi_layer_get_csc_mode(const struct drm_format_info * format)2450ee9f600SJernej Skrabec static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
246daab3d0eSJernej Skrabec {
247daab3d0eSJernej Skrabec if (!format->is_yuv)
248daab3d0eSJernej Skrabec return SUN8I_CSC_MODE_OFF;
249daab3d0eSJernej Skrabec
250daab3d0eSJernej Skrabec switch (format->format) {
251daab3d0eSJernej Skrabec case DRM_FORMAT_YVU411:
252daab3d0eSJernej Skrabec case DRM_FORMAT_YVU420:
253daab3d0eSJernej Skrabec case DRM_FORMAT_YVU422:
254daab3d0eSJernej Skrabec case DRM_FORMAT_YVU444:
255daab3d0eSJernej Skrabec return SUN8I_CSC_MODE_YVU2RGB;
256daab3d0eSJernej Skrabec default:
257daab3d0eSJernej Skrabec return SUN8I_CSC_MODE_YUV2RGB;
258daab3d0eSJernej Skrabec }
259daab3d0eSJernej Skrabec }
260daab3d0eSJernej Skrabec
sun8i_vi_layer_update_formats(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane)2617480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
2627480ba4dSJernej Skrabec int overlay, struct drm_plane *plane)
2637480ba4dSJernej Skrabec {
2647480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state;
2657831112eSJernej Skrabec u32 val, ch_base, csc_mode, hw_fmt;
266a9a75359SJernej Skrabec const struct drm_format_info *fmt;
2677831112eSJernej Skrabec int ret;
2684b09c073SJernej Skrabec
2694b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel);
2707480ba4dSJernej Skrabec
271a9a75359SJernej Skrabec fmt = state->fb->format;
2727831112eSJernej Skrabec ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
2737831112eSJernej Skrabec if (ret) {
2747480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Invalid format\n");
2757831112eSJernej Skrabec return ret;
2767480ba4dSJernej Skrabec }
2777480ba4dSJernej Skrabec
2787831112eSJernej Skrabec val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
2797480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs,
2804b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
281e1ef9006SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
282e1ef9006SJernej Skrabec
283daab3d0eSJernej Skrabec csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
284daab3d0eSJernej Skrabec if (csc_mode != SUN8I_CSC_MODE_OFF) {
285daab3d0eSJernej Skrabec sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
286b72cb0dcSJernej Skrabec state->color_encoding,
287b72cb0dcSJernej Skrabec state->color_range);
288e1ef9006SJernej Skrabec sun8i_csc_enable_ccsc(mixer, channel, true);
289e1ef9006SJernej Skrabec } else {
290e1ef9006SJernej Skrabec sun8i_csc_enable_ccsc(mixer, channel, false);
291e1ef9006SJernej Skrabec }
292e1ef9006SJernej Skrabec
293a9a75359SJernej Skrabec if (!fmt->is_yuv)
294e1ef9006SJernej Skrabec val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
295e1ef9006SJernej Skrabec else
296e1ef9006SJernej Skrabec val = 0;
297e1ef9006SJernej Skrabec
298e1ef9006SJernej Skrabec regmap_update_bits(mixer->engine.regs,
2994b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
300e1ef9006SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
3017480ba4dSJernej Skrabec
3027480ba4dSJernej Skrabec return 0;
3037480ba4dSJernej Skrabec }
3047480ba4dSJernej Skrabec
sun8i_vi_layer_update_buffer(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane)3057480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
3067480ba4dSJernej Skrabec int overlay, struct drm_plane *plane)
3077480ba4dSJernej Skrabec {
3087480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state;
3097480ba4dSJernej Skrabec struct drm_framebuffer *fb = state->fb;
310e1ef9006SJernej Skrabec const struct drm_format_info *format = fb->format;
3114a83c26aSDanilo Krummrich struct drm_gem_dma_object *gem;
312e1ef9006SJernej Skrabec u32 dx, dy, src_x, src_y;
313*8c30eeccSDanilo Krummrich dma_addr_t dma_addr;
3144b09c073SJernej Skrabec u32 ch_base;
315e1ef9006SJernej Skrabec int i;
3167480ba4dSJernej Skrabec
3174b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel);
3184b09c073SJernej Skrabec
319e1ef9006SJernej Skrabec /* Adjust x and y to be dividable by subsampling factor */
320e1ef9006SJernej Skrabec src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
321e1ef9006SJernej Skrabec src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
322e1ef9006SJernej Skrabec
323e1ef9006SJernej Skrabec for (i = 0; i < format->num_planes; i++) {
3247480ba4dSJernej Skrabec /* Get the physical address of the buffer in memory */
3256bcfe8eaSDanilo Krummrich gem = drm_fb_dma_get_gem_obj(fb, i);
3267480ba4dSJernej Skrabec
327*8c30eeccSDanilo Krummrich DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
3287480ba4dSJernej Skrabec
3297480ba4dSJernej Skrabec /* Compute the start of the displayed memory */
330*8c30eeccSDanilo Krummrich dma_addr = gem->dma_addr + fb->offsets[i];
331e1ef9006SJernej Skrabec
332e1ef9006SJernej Skrabec dx = src_x;
333e1ef9006SJernej Skrabec dy = src_y;
334e1ef9006SJernej Skrabec
335e1ef9006SJernej Skrabec if (i > 0) {
336e1ef9006SJernej Skrabec dx /= format->hsub;
337e1ef9006SJernej Skrabec dy /= format->vsub;
338e1ef9006SJernej Skrabec }
3397480ba4dSJernej Skrabec
3407480ba4dSJernej Skrabec /* Fixup framebuffer address for src coordinates */
341*8c30eeccSDanilo Krummrich dma_addr += dx * format->cpp[i];
342*8c30eeccSDanilo Krummrich dma_addr += dy * fb->pitches[i];
3437480ba4dSJernej Skrabec
3447480ba4dSJernej Skrabec /* Set the line width */
345e1ef9006SJernej Skrabec DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
346e1ef9006SJernej Skrabec i + 1, fb->pitches[i]);
3477480ba4dSJernej Skrabec regmap_write(mixer->engine.regs,
3484b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
349e1ef9006SJernej Skrabec overlay, i),
350e1ef9006SJernej Skrabec fb->pitches[i]);
3517480ba4dSJernej Skrabec
352e1ef9006SJernej Skrabec DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
353*8c30eeccSDanilo Krummrich i + 1, &dma_addr);
3547480ba4dSJernej Skrabec
3557480ba4dSJernej Skrabec regmap_write(mixer->engine.regs,
3564b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
357e1ef9006SJernej Skrabec overlay, i),
358*8c30eeccSDanilo Krummrich lower_32_bits(dma_addr));
359e1ef9006SJernej Skrabec }
3607480ba4dSJernej Skrabec
3617480ba4dSJernej Skrabec return 0;
3627480ba4dSJernej Skrabec }
3637480ba4dSJernej Skrabec
sun8i_vi_layer_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)3647480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
3657c11b99aSMaxime Ripard struct drm_atomic_state *state)
3667480ba4dSJernej Skrabec {
3677c11b99aSMaxime Ripard struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
3687c11b99aSMaxime Ripard plane);
369b862a648SJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
370ba5c1649SMaxime Ripard struct drm_crtc *crtc = new_plane_state->crtc;
3717480ba4dSJernej Skrabec struct drm_crtc_state *crtc_state;
372b862a648SJernej Skrabec int min_scale, max_scale;
3737480ba4dSJernej Skrabec
3747480ba4dSJernej Skrabec if (!crtc)
3757480ba4dSJernej Skrabec return 0;
3767480ba4dSJernej Skrabec
377dec92020SMaxime Ripard crtc_state = drm_atomic_get_existing_crtc_state(state,
378ba5c1649SMaxime Ripard crtc);
3797480ba4dSJernej Skrabec if (WARN_ON(!crtc_state))
3807480ba4dSJernej Skrabec return -EINVAL;
3817480ba4dSJernej Skrabec
382cce32e4eSThomas Zimmermann min_scale = DRM_PLANE_NO_SCALING;
383cce32e4eSThomas Zimmermann max_scale = DRM_PLANE_NO_SCALING;
384bc29489fSJernej Skrabec
385b862a648SJernej Skrabec if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
386b862a648SJernej Skrabec min_scale = SUN8I_VI_SCALER_SCALE_MIN;
387b862a648SJernej Skrabec max_scale = SUN8I_VI_SCALER_SCALE_MAX;
388b862a648SJernej Skrabec }
389b862a648SJernej Skrabec
390ba5c1649SMaxime Ripard return drm_atomic_helper_check_plane_state(new_plane_state,
391ba5c1649SMaxime Ripard crtc_state,
392b862a648SJernej Skrabec min_scale, max_scale,
3937480ba4dSJernej Skrabec true, true);
3947480ba4dSJernej Skrabec }
3957480ba4dSJernej Skrabec
sun8i_vi_layer_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)3967480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
397977697e2SMaxime Ripard struct drm_atomic_state *state)
3987480ba4dSJernej Skrabec {
399977697e2SMaxime Ripard struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
400977697e2SMaxime Ripard plane);
4017480ba4dSJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
402d8b3f454SPaul Kocialkowski unsigned int old_zpos = old_state->normalized_zpos;
4037480ba4dSJernej Skrabec struct sun8i_mixer *mixer = layer->mixer;
4047480ba4dSJernej Skrabec
405d8b3f454SPaul Kocialkowski sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
406d8b3f454SPaul Kocialkowski old_zpos);
4077480ba4dSJernej Skrabec }
4087480ba4dSJernej Skrabec
sun8i_vi_layer_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)4097480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
410977697e2SMaxime Ripard struct drm_atomic_state *state)
4117480ba4dSJernej Skrabec {
412977697e2SMaxime Ripard struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
413977697e2SMaxime Ripard plane);
41437418bf1SMaxime Ripard struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
41537418bf1SMaxime Ripard plane);
4167480ba4dSJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
417e05162c0SMaxime Ripard unsigned int zpos = new_state->normalized_zpos;
418d8b3f454SPaul Kocialkowski unsigned int old_zpos = old_state->normalized_zpos;
4197480ba4dSJernej Skrabec struct sun8i_mixer *mixer = layer->mixer;
4207480ba4dSJernej Skrabec
421e05162c0SMaxime Ripard if (!new_state->visible) {
4227480ba4dSJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel,
423d8b3f454SPaul Kocialkowski layer->overlay, false, 0, old_zpos);
4247480ba4dSJernej Skrabec return;
4257480ba4dSJernej Skrabec }
4267480ba4dSJernej Skrabec
4277480ba4dSJernej Skrabec sun8i_vi_layer_update_coord(mixer, layer->channel,
428f88c5ee7SJernej Skrabec layer->overlay, plane, zpos);
429c0f6f0c4SRoman Stratiienko sun8i_vi_layer_update_alpha(mixer, layer->channel,
430c0f6f0c4SRoman Stratiienko layer->overlay, plane);
4317480ba4dSJernej Skrabec sun8i_vi_layer_update_formats(mixer, layer->channel,
4327480ba4dSJernej Skrabec layer->overlay, plane);
4337480ba4dSJernej Skrabec sun8i_vi_layer_update_buffer(mixer, layer->channel,
4347480ba4dSJernej Skrabec layer->overlay, plane);
435f88c5ee7SJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
436d8b3f454SPaul Kocialkowski true, zpos, old_zpos);
4377480ba4dSJernej Skrabec }
4387480ba4dSJernej Skrabec
439f13478c9SRikard Falkeborn static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
4407480ba4dSJernej Skrabec .atomic_check = sun8i_vi_layer_atomic_check,
4417480ba4dSJernej Skrabec .atomic_disable = sun8i_vi_layer_atomic_disable,
4427480ba4dSJernej Skrabec .atomic_update = sun8i_vi_layer_atomic_update,
4437480ba4dSJernej Skrabec };
4447480ba4dSJernej Skrabec
4457480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
4467480ba4dSJernej Skrabec .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
4477480ba4dSJernej Skrabec .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
4487480ba4dSJernej Skrabec .destroy = drm_plane_cleanup,
4497480ba4dSJernej Skrabec .disable_plane = drm_atomic_helper_disable_plane,
4507480ba4dSJernej Skrabec .reset = drm_atomic_helper_plane_reset,
4517480ba4dSJernej Skrabec .update_plane = drm_atomic_helper_update_plane,
4527480ba4dSJernej Skrabec };
4537480ba4dSJernej Skrabec
4547480ba4dSJernej Skrabec /*
45520896ef1SJernej Skrabec * While DE2 VI layer supports same RGB formats as UI layer, alpha
45620896ef1SJernej Skrabec * channel is ignored. This structure lists all unique variants
45720896ef1SJernej Skrabec * where alpha channel is replaced with "don't care" (X) channel.
4587480ba4dSJernej Skrabec */
4597480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = {
4607480ba4dSJernej Skrabec DRM_FORMAT_BGR565,
4617480ba4dSJernej Skrabec DRM_FORMAT_BGR888,
46220896ef1SJernej Skrabec DRM_FORMAT_BGRX4444,
46320896ef1SJernej Skrabec DRM_FORMAT_BGRX5551,
4647480ba4dSJernej Skrabec DRM_FORMAT_BGRX8888,
4657480ba4dSJernej Skrabec DRM_FORMAT_RGB565,
4667480ba4dSJernej Skrabec DRM_FORMAT_RGB888,
46720896ef1SJernej Skrabec DRM_FORMAT_RGBX4444,
46820896ef1SJernej Skrabec DRM_FORMAT_RGBX5551,
4697480ba4dSJernej Skrabec DRM_FORMAT_RGBX8888,
47020896ef1SJernej Skrabec DRM_FORMAT_XBGR1555,
47120896ef1SJernej Skrabec DRM_FORMAT_XBGR4444,
4727480ba4dSJernej Skrabec DRM_FORMAT_XBGR8888,
47320896ef1SJernej Skrabec DRM_FORMAT_XRGB1555,
47420896ef1SJernej Skrabec DRM_FORMAT_XRGB4444,
4757480ba4dSJernej Skrabec DRM_FORMAT_XRGB8888,
476e1ef9006SJernej Skrabec
477e1ef9006SJernej Skrabec DRM_FORMAT_NV16,
478e1ef9006SJernej Skrabec DRM_FORMAT_NV12,
479e1ef9006SJernej Skrabec DRM_FORMAT_NV21,
480e1ef9006SJernej Skrabec DRM_FORMAT_NV61,
481e1ef9006SJernej Skrabec DRM_FORMAT_UYVY,
482e1ef9006SJernej Skrabec DRM_FORMAT_VYUY,
483e1ef9006SJernej Skrabec DRM_FORMAT_YUYV,
484e1ef9006SJernej Skrabec DRM_FORMAT_YVYU,
485e1ef9006SJernej Skrabec DRM_FORMAT_YUV411,
486e1ef9006SJernej Skrabec DRM_FORMAT_YUV420,
487e1ef9006SJernej Skrabec DRM_FORMAT_YUV422,
488e1ef9006SJernej Skrabec DRM_FORMAT_YVU411,
489e1ef9006SJernej Skrabec DRM_FORMAT_YVU420,
490e1ef9006SJernej Skrabec DRM_FORMAT_YVU422,
4917480ba4dSJernej Skrabec };
4927480ba4dSJernej Skrabec
493169ca4b3SJernej Skrabec static const u32 sun8i_vi_layer_de3_formats[] = {
494169ca4b3SJernej Skrabec DRM_FORMAT_ABGR1555,
495169ca4b3SJernej Skrabec DRM_FORMAT_ABGR2101010,
496169ca4b3SJernej Skrabec DRM_FORMAT_ABGR4444,
497169ca4b3SJernej Skrabec DRM_FORMAT_ABGR8888,
498169ca4b3SJernej Skrabec DRM_FORMAT_ARGB1555,
499169ca4b3SJernej Skrabec DRM_FORMAT_ARGB2101010,
500169ca4b3SJernej Skrabec DRM_FORMAT_ARGB4444,
501169ca4b3SJernej Skrabec DRM_FORMAT_ARGB8888,
502169ca4b3SJernej Skrabec DRM_FORMAT_BGR565,
503169ca4b3SJernej Skrabec DRM_FORMAT_BGR888,
504169ca4b3SJernej Skrabec DRM_FORMAT_BGRA1010102,
505169ca4b3SJernej Skrabec DRM_FORMAT_BGRA5551,
506169ca4b3SJernej Skrabec DRM_FORMAT_BGRA4444,
507169ca4b3SJernej Skrabec DRM_FORMAT_BGRA8888,
508169ca4b3SJernej Skrabec DRM_FORMAT_BGRX8888,
509169ca4b3SJernej Skrabec DRM_FORMAT_RGB565,
510169ca4b3SJernej Skrabec DRM_FORMAT_RGB888,
511169ca4b3SJernej Skrabec DRM_FORMAT_RGBA1010102,
512169ca4b3SJernej Skrabec DRM_FORMAT_RGBA4444,
513169ca4b3SJernej Skrabec DRM_FORMAT_RGBA5551,
514169ca4b3SJernej Skrabec DRM_FORMAT_RGBA8888,
515169ca4b3SJernej Skrabec DRM_FORMAT_RGBX8888,
516169ca4b3SJernej Skrabec DRM_FORMAT_XBGR8888,
517169ca4b3SJernej Skrabec DRM_FORMAT_XRGB8888,
518169ca4b3SJernej Skrabec
519169ca4b3SJernej Skrabec DRM_FORMAT_NV16,
520169ca4b3SJernej Skrabec DRM_FORMAT_NV12,
521169ca4b3SJernej Skrabec DRM_FORMAT_NV21,
522169ca4b3SJernej Skrabec DRM_FORMAT_NV61,
523169ca4b3SJernej Skrabec DRM_FORMAT_P010,
524169ca4b3SJernej Skrabec DRM_FORMAT_P210,
525169ca4b3SJernej Skrabec DRM_FORMAT_UYVY,
526169ca4b3SJernej Skrabec DRM_FORMAT_VYUY,
527169ca4b3SJernej Skrabec DRM_FORMAT_YUYV,
528169ca4b3SJernej Skrabec DRM_FORMAT_YVYU,
529169ca4b3SJernej Skrabec DRM_FORMAT_YUV411,
530169ca4b3SJernej Skrabec DRM_FORMAT_YUV420,
531169ca4b3SJernej Skrabec DRM_FORMAT_YUV422,
532169ca4b3SJernej Skrabec DRM_FORMAT_YVU411,
533169ca4b3SJernej Skrabec DRM_FORMAT_YVU420,
534169ca4b3SJernej Skrabec DRM_FORMAT_YVU422,
535169ca4b3SJernej Skrabec };
536169ca4b3SJernej Skrabec
537b36a6bb8SPiotr Oniszczuk static const uint64_t sun8i_layer_modifiers[] = {
538b36a6bb8SPiotr Oniszczuk DRM_FORMAT_MOD_LINEAR,
539b36a6bb8SPiotr Oniszczuk DRM_FORMAT_MOD_INVALID
540b36a6bb8SPiotr Oniszczuk };
541b36a6bb8SPiotr Oniszczuk
sun8i_vi_layer_init_one(struct drm_device * drm,struct sun8i_mixer * mixer,int index)5427480ba4dSJernej Skrabec struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
5437480ba4dSJernej Skrabec struct sun8i_mixer *mixer,
5447480ba4dSJernej Skrabec int index)
5457480ba4dSJernej Skrabec {
546c3779dabSSamuel Holland enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
5475917e0bdSJernej Skrabec u32 supported_encodings, supported_ranges;
548169ca4b3SJernej Skrabec unsigned int plane_cnt, format_count;
5497480ba4dSJernej Skrabec struct sun8i_vi_layer *layer;
550169ca4b3SJernej Skrabec const u32 *formats;
5517480ba4dSJernej Skrabec int ret;
5527480ba4dSJernej Skrabec
5537480ba4dSJernej Skrabec layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
5547480ba4dSJernej Skrabec if (!layer)
5557480ba4dSJernej Skrabec return ERR_PTR(-ENOMEM);
5567480ba4dSJernej Skrabec
557169ca4b3SJernej Skrabec if (mixer->cfg->is_de3) {
558169ca4b3SJernej Skrabec formats = sun8i_vi_layer_de3_formats;
559169ca4b3SJernej Skrabec format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
560169ca4b3SJernej Skrabec } else {
561169ca4b3SJernej Skrabec formats = sun8i_vi_layer_formats;
562169ca4b3SJernej Skrabec format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
563169ca4b3SJernej Skrabec }
564169ca4b3SJernej Skrabec
565c3779dabSSamuel Holland if (!mixer->cfg->ui_num && index == 0)
566c3779dabSSamuel Holland type = DRM_PLANE_TYPE_PRIMARY;
567c3779dabSSamuel Holland
5687480ba4dSJernej Skrabec /* possible crtcs are set later */
5697480ba4dSJernej Skrabec ret = drm_universal_plane_init(drm, &layer->plane, 0,
5707480ba4dSJernej Skrabec &sun8i_vi_layer_funcs,
571169ca4b3SJernej Skrabec formats, format_count,
572b36a6bb8SPiotr Oniszczuk sun8i_layer_modifiers,
573c3779dabSSamuel Holland type, NULL);
5747480ba4dSJernej Skrabec if (ret) {
5757480ba4dSJernej Skrabec dev_err(drm->dev, "Couldn't initialize layer\n");
5767480ba4dSJernej Skrabec return ERR_PTR(ret);
5777480ba4dSJernej Skrabec }
5787480ba4dSJernej Skrabec
579f88c5ee7SJernej Skrabec plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
580f88c5ee7SJernej Skrabec
581c0f6f0c4SRoman Stratiienko if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
582c0f6f0c4SRoman Stratiienko ret = drm_plane_create_alpha_property(&layer->plane);
583c0f6f0c4SRoman Stratiienko if (ret) {
584c0f6f0c4SRoman Stratiienko dev_err(drm->dev, "Couldn't add alpha property\n");
585c0f6f0c4SRoman Stratiienko return ERR_PTR(ret);
586c0f6f0c4SRoman Stratiienko }
587c0f6f0c4SRoman Stratiienko }
588c0f6f0c4SRoman Stratiienko
589f88c5ee7SJernej Skrabec ret = drm_plane_create_zpos_property(&layer->plane, index,
590f88c5ee7SJernej Skrabec 0, plane_cnt - 1);
5917480ba4dSJernej Skrabec if (ret) {
5927480ba4dSJernej Skrabec dev_err(drm->dev, "Couldn't add zpos property\n");
5937480ba4dSJernej Skrabec return ERR_PTR(ret);
5947480ba4dSJernej Skrabec }
5957480ba4dSJernej Skrabec
5965917e0bdSJernej Skrabec supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
5975917e0bdSJernej Skrabec BIT(DRM_COLOR_YCBCR_BT709);
5980cf1d704SJernej Skrabec if (mixer->cfg->is_de3)
5990cf1d704SJernej Skrabec supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
6005917e0bdSJernej Skrabec
6015917e0bdSJernej Skrabec supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
6025917e0bdSJernej Skrabec BIT(DRM_COLOR_YCBCR_FULL_RANGE);
6035917e0bdSJernej Skrabec
6045917e0bdSJernej Skrabec ret = drm_plane_create_color_properties(&layer->plane,
6055917e0bdSJernej Skrabec supported_encodings,
6065917e0bdSJernej Skrabec supported_ranges,
6075917e0bdSJernej Skrabec DRM_COLOR_YCBCR_BT709,
6085917e0bdSJernej Skrabec DRM_COLOR_YCBCR_LIMITED_RANGE);
6095917e0bdSJernej Skrabec if (ret) {
6105917e0bdSJernej Skrabec dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
6115917e0bdSJernej Skrabec return ERR_PTR(ret);
6125917e0bdSJernej Skrabec }
6135917e0bdSJernej Skrabec
6147480ba4dSJernej Skrabec drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
6157480ba4dSJernej Skrabec layer->mixer = mixer;
6167480ba4dSJernej Skrabec layer->channel = index;
6177480ba4dSJernej Skrabec layer->overlay = 0;
6187480ba4dSJernej Skrabec
6197480ba4dSJernej Skrabec return layer;
6207480ba4dSJernej Skrabec }
621