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