Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
0004  */
0005 
0006 #include <drm/drm_atomic.h>
0007 #include <drm/drm_atomic_helper.h>
0008 #include <drm/drm_blend.h>
0009 #include <drm/drm_crtc.h>
0010 #include <drm/drm_fb_cma_helper.h>
0011 #include <drm/drm_framebuffer.h>
0012 #include <drm/drm_gem_atomic_helper.h>
0013 #include <drm/drm_gem_cma_helper.h>
0014 #include <drm/drm_plane_helper.h>
0015 #include <drm/drm_probe_helper.h>
0016 
0017 #include "sun8i_csc.h"
0018 #include "sun8i_mixer.h"
0019 #include "sun8i_vi_layer.h"
0020 #include "sun8i_vi_scaler.h"
0021 
0022 static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
0023                   int overlay, bool enable, unsigned int zpos,
0024                   unsigned int old_zpos)
0025 {
0026     u32 val, bld_base, ch_base;
0027 
0028     bld_base = sun8i_blender_base(mixer);
0029     ch_base = sun8i_channel_base(mixer, channel);
0030 
0031     DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
0032              enable ? "En" : "Dis", channel, overlay);
0033 
0034     if (enable)
0035         val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
0036     else
0037         val = 0;
0038 
0039     regmap_update_bits(mixer->engine.regs,
0040                SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
0041                SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
0042 
0043     if (!enable || zpos != old_zpos) {
0044         regmap_update_bits(mixer->engine.regs,
0045                    SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
0046                    SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
0047                    0);
0048 
0049         regmap_update_bits(mixer->engine.regs,
0050                    SUN8I_MIXER_BLEND_ROUTE(bld_base),
0051                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
0052                    0);
0053     }
0054 
0055     if (enable) {
0056         val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
0057 
0058         regmap_update_bits(mixer->engine.regs,
0059                    SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
0060                    val, val);
0061 
0062         val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
0063 
0064         regmap_update_bits(mixer->engine.regs,
0065                    SUN8I_MIXER_BLEND_ROUTE(bld_base),
0066                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
0067                    val);
0068     }
0069 }
0070 
0071 static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
0072                     int overlay, struct drm_plane *plane)
0073 {
0074     u32 mask, val, ch_base;
0075 
0076     ch_base = sun8i_channel_base(mixer, channel);
0077 
0078     if (mixer->cfg->is_de3) {
0079         mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
0080                SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
0081         val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
0082             (plane->state->alpha >> 8);
0083 
0084         val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
0085             SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
0086             SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
0087 
0088         regmap_update_bits(mixer->engine.regs,
0089                    SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
0090                                   overlay),
0091                    mask, val);
0092     } else if (mixer->cfg->vi_num == 1) {
0093         regmap_update_bits(mixer->engine.regs,
0094                    SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
0095                    SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
0096                    SUN8I_MIXER_FCC_GLOBAL_ALPHA
0097                     (plane->state->alpha >> 8));
0098     }
0099 }
0100 
0101 static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
0102                        int overlay, struct drm_plane *plane,
0103                        unsigned int zpos)
0104 {
0105     struct drm_plane_state *state = plane->state;
0106     const struct drm_format_info *format = state->fb->format;
0107     u32 src_w, src_h, dst_w, dst_h;
0108     u32 bld_base, ch_base;
0109     u32 outsize, insize;
0110     u32 hphase, vphase;
0111     u32 hn = 0, hm = 0;
0112     u32 vn = 0, vm = 0;
0113     bool subsampled;
0114 
0115     DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
0116              channel, overlay);
0117 
0118     bld_base = sun8i_blender_base(mixer);
0119     ch_base = sun8i_channel_base(mixer, channel);
0120 
0121     src_w = drm_rect_width(&state->src) >> 16;
0122     src_h = drm_rect_height(&state->src) >> 16;
0123     dst_w = drm_rect_width(&state->dst);
0124     dst_h = drm_rect_height(&state->dst);
0125 
0126     hphase = state->src.x1 & 0xffff;
0127     vphase = state->src.y1 & 0xffff;
0128 
0129     /* make coordinates dividable by subsampling factor */
0130     if (format->hsub > 1) {
0131         int mask, remainder;
0132 
0133         mask = format->hsub - 1;
0134         remainder = (state->src.x1 >> 16) & mask;
0135         src_w = (src_w + remainder) & ~mask;
0136         hphase += remainder << 16;
0137     }
0138 
0139     if (format->vsub > 1) {
0140         int mask, remainder;
0141 
0142         mask = format->vsub - 1;
0143         remainder = (state->src.y1 >> 16) & mask;
0144         src_h = (src_h + remainder) & ~mask;
0145         vphase += remainder << 16;
0146     }
0147 
0148     insize = SUN8I_MIXER_SIZE(src_w, src_h);
0149     outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
0150 
0151     /* Set height and width */
0152     DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
0153              (state->src.x1 >> 16) & ~(format->hsub - 1),
0154              (state->src.y1 >> 16) & ~(format->vsub - 1));
0155     DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
0156     regmap_write(mixer->engine.regs,
0157              SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
0158              insize);
0159     regmap_write(mixer->engine.regs,
0160              SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
0161              insize);
0162 
0163     /*
0164      * Scaler must be enabled for subsampled formats, so it scales
0165      * chroma to same size as luma.
0166      */
0167     subsampled = format->hsub > 1 || format->vsub > 1;
0168 
0169     if (insize != outsize || subsampled || hphase || vphase) {
0170         unsigned int scanline, required;
0171         struct drm_display_mode *mode;
0172         u32 hscale, vscale, fps;
0173         u64 ability;
0174 
0175         DRM_DEBUG_DRIVER("HW scaling is enabled\n");
0176 
0177         mode = &plane->state->crtc->state->mode;
0178         fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
0179         ability = clk_get_rate(mixer->mod_clk);
0180         /* BSP algorithm assumes 80% efficiency of VI scaler unit */
0181         ability *= 80;
0182         do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
0183 
0184         required = src_h * 100 / dst_h;
0185 
0186         if (ability < required) {
0187             DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
0188             vm = src_h;
0189             vn = (u32)ability * dst_h / 100;
0190             src_h = vn;
0191         }
0192 
0193         /* it seems that every RGB scaler has buffer for 2048 pixels */
0194         scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
0195 
0196         if (src_w > scanline) {
0197             DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
0198             hm = src_w;
0199             hn = scanline;
0200             src_w = hn;
0201         }
0202 
0203         hscale = (src_w << 16) / dst_w;
0204         vscale = (src_h << 16) / dst_h;
0205 
0206         sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
0207                       dst_h, hscale, vscale, hphase, vphase,
0208                       format);
0209         sun8i_vi_scaler_enable(mixer, channel, true);
0210     } else {
0211         DRM_DEBUG_DRIVER("HW scaling is not needed\n");
0212         sun8i_vi_scaler_enable(mixer, channel, false);
0213     }
0214 
0215     regmap_write(mixer->engine.regs,
0216              SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
0217              SUN8I_MIXER_CHAN_VI_DS_N(hn) |
0218              SUN8I_MIXER_CHAN_VI_DS_M(hm));
0219     regmap_write(mixer->engine.regs,
0220              SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
0221              SUN8I_MIXER_CHAN_VI_DS_N(hn) |
0222              SUN8I_MIXER_CHAN_VI_DS_M(hm));
0223     regmap_write(mixer->engine.regs,
0224              SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
0225              SUN8I_MIXER_CHAN_VI_DS_N(vn) |
0226              SUN8I_MIXER_CHAN_VI_DS_M(vm));
0227     regmap_write(mixer->engine.regs,
0228              SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
0229              SUN8I_MIXER_CHAN_VI_DS_N(vn) |
0230              SUN8I_MIXER_CHAN_VI_DS_M(vm));
0231 
0232     /* Set base coordinates */
0233     DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
0234              state->dst.x1, state->dst.y1);
0235     DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
0236     regmap_write(mixer->engine.regs,
0237              SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
0238              SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
0239     regmap_write(mixer->engine.regs,
0240              SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
0241              outsize);
0242 
0243     return 0;
0244 }
0245 
0246 static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
0247 {
0248     if (!format->is_yuv)
0249         return SUN8I_CSC_MODE_OFF;
0250 
0251     switch (format->format) {
0252     case DRM_FORMAT_YVU411:
0253     case DRM_FORMAT_YVU420:
0254     case DRM_FORMAT_YVU422:
0255     case DRM_FORMAT_YVU444:
0256         return SUN8I_CSC_MODE_YVU2RGB;
0257     default:
0258         return SUN8I_CSC_MODE_YUV2RGB;
0259     }
0260 }
0261 
0262 static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
0263                      int overlay, struct drm_plane *plane)
0264 {
0265     struct drm_plane_state *state = plane->state;
0266     u32 val, ch_base, csc_mode, hw_fmt;
0267     const struct drm_format_info *fmt;
0268     int ret;
0269 
0270     ch_base = sun8i_channel_base(mixer, channel);
0271 
0272     fmt = state->fb->format;
0273     ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
0274     if (ret) {
0275         DRM_DEBUG_DRIVER("Invalid format\n");
0276         return ret;
0277     }
0278 
0279     val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
0280     regmap_update_bits(mixer->engine.regs,
0281                SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
0282                SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
0283 
0284     csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
0285     if (csc_mode != SUN8I_CSC_MODE_OFF) {
0286         sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
0287                         state->color_encoding,
0288                         state->color_range);
0289         sun8i_csc_enable_ccsc(mixer, channel, true);
0290     } else {
0291         sun8i_csc_enable_ccsc(mixer, channel, false);
0292     }
0293 
0294     if (!fmt->is_yuv)
0295         val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
0296     else
0297         val = 0;
0298 
0299     regmap_update_bits(mixer->engine.regs,
0300                SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
0301                SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
0302 
0303     return 0;
0304 }
0305 
0306 static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
0307                     int overlay, struct drm_plane *plane)
0308 {
0309     struct drm_plane_state *state = plane->state;
0310     struct drm_framebuffer *fb = state->fb;
0311     const struct drm_format_info *format = fb->format;
0312     struct drm_gem_cma_object *gem;
0313     u32 dx, dy, src_x, src_y;
0314     dma_addr_t paddr;
0315     u32 ch_base;
0316     int i;
0317 
0318     ch_base = sun8i_channel_base(mixer, channel);
0319 
0320     /* Adjust x and y to be dividable by subsampling factor */
0321     src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
0322     src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
0323 
0324     for (i = 0; i < format->num_planes; i++) {
0325         /* Get the physical address of the buffer in memory */
0326         gem = drm_fb_cma_get_gem_obj(fb, i);
0327 
0328         DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
0329 
0330         /* Compute the start of the displayed memory */
0331         paddr = gem->paddr + fb->offsets[i];
0332 
0333         dx = src_x;
0334         dy = src_y;
0335 
0336         if (i > 0) {
0337             dx /= format->hsub;
0338             dy /= format->vsub;
0339         }
0340 
0341         /* Fixup framebuffer address for src coordinates */
0342         paddr += dx * format->cpp[i];
0343         paddr += dy * fb->pitches[i];
0344 
0345         /* Set the line width */
0346         DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
0347                  i + 1, fb->pitches[i]);
0348         regmap_write(mixer->engine.regs,
0349                  SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
0350                                  overlay, i),
0351                  fb->pitches[i]);
0352 
0353         DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
0354                  i + 1, &paddr);
0355 
0356         regmap_write(mixer->engine.regs,
0357                  SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
0358                                  overlay, i),
0359                  lower_32_bits(paddr));
0360     }
0361 
0362     return 0;
0363 }
0364 
0365 static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
0366                        struct drm_atomic_state *state)
0367 {
0368     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0369                                          plane);
0370     struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
0371     struct drm_crtc *crtc = new_plane_state->crtc;
0372     struct drm_crtc_state *crtc_state;
0373     int min_scale, max_scale;
0374 
0375     if (!crtc)
0376         return 0;
0377 
0378     crtc_state = drm_atomic_get_existing_crtc_state(state,
0379                             crtc);
0380     if (WARN_ON(!crtc_state))
0381         return -EINVAL;
0382 
0383     min_scale = DRM_PLANE_HELPER_NO_SCALING;
0384     max_scale = DRM_PLANE_HELPER_NO_SCALING;
0385 
0386     if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
0387         min_scale = SUN8I_VI_SCALER_SCALE_MIN;
0388         max_scale = SUN8I_VI_SCALER_SCALE_MAX;
0389     }
0390 
0391     return drm_atomic_helper_check_plane_state(new_plane_state,
0392                            crtc_state,
0393                            min_scale, max_scale,
0394                            true, true);
0395 }
0396 
0397 static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
0398                       struct drm_atomic_state *state)
0399 {
0400     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0401                                        plane);
0402     struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
0403     unsigned int old_zpos = old_state->normalized_zpos;
0404     struct sun8i_mixer *mixer = layer->mixer;
0405 
0406     sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
0407                   old_zpos);
0408 }
0409 
0410 static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
0411                      struct drm_atomic_state *state)
0412 {
0413     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0414                                        plane);
0415     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0416                                        plane);
0417     struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
0418     unsigned int zpos = new_state->normalized_zpos;
0419     unsigned int old_zpos = old_state->normalized_zpos;
0420     struct sun8i_mixer *mixer = layer->mixer;
0421 
0422     if (!new_state->visible) {
0423         sun8i_vi_layer_enable(mixer, layer->channel,
0424                       layer->overlay, false, 0, old_zpos);
0425         return;
0426     }
0427 
0428     sun8i_vi_layer_update_coord(mixer, layer->channel,
0429                     layer->overlay, plane, zpos);
0430     sun8i_vi_layer_update_alpha(mixer, layer->channel,
0431                     layer->overlay, plane);
0432     sun8i_vi_layer_update_formats(mixer, layer->channel,
0433                       layer->overlay, plane);
0434     sun8i_vi_layer_update_buffer(mixer, layer->channel,
0435                      layer->overlay, plane);
0436     sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
0437                   true, zpos, old_zpos);
0438 }
0439 
0440 static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
0441     .atomic_check   = sun8i_vi_layer_atomic_check,
0442     .atomic_disable = sun8i_vi_layer_atomic_disable,
0443     .atomic_update  = sun8i_vi_layer_atomic_update,
0444 };
0445 
0446 static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
0447     .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
0448     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
0449     .destroy        = drm_plane_cleanup,
0450     .disable_plane      = drm_atomic_helper_disable_plane,
0451     .reset          = drm_atomic_helper_plane_reset,
0452     .update_plane       = drm_atomic_helper_update_plane,
0453 };
0454 
0455 /*
0456  * While DE2 VI layer supports same RGB formats as UI layer, alpha
0457  * channel is ignored. This structure lists all unique variants
0458  * where alpha channel is replaced with "don't care" (X) channel.
0459  */
0460 static const u32 sun8i_vi_layer_formats[] = {
0461     DRM_FORMAT_BGR565,
0462     DRM_FORMAT_BGR888,
0463     DRM_FORMAT_BGRX4444,
0464     DRM_FORMAT_BGRX5551,
0465     DRM_FORMAT_BGRX8888,
0466     DRM_FORMAT_RGB565,
0467     DRM_FORMAT_RGB888,
0468     DRM_FORMAT_RGBX4444,
0469     DRM_FORMAT_RGBX5551,
0470     DRM_FORMAT_RGBX8888,
0471     DRM_FORMAT_XBGR1555,
0472     DRM_FORMAT_XBGR4444,
0473     DRM_FORMAT_XBGR8888,
0474     DRM_FORMAT_XRGB1555,
0475     DRM_FORMAT_XRGB4444,
0476     DRM_FORMAT_XRGB8888,
0477 
0478     DRM_FORMAT_NV16,
0479     DRM_FORMAT_NV12,
0480     DRM_FORMAT_NV21,
0481     DRM_FORMAT_NV61,
0482     DRM_FORMAT_UYVY,
0483     DRM_FORMAT_VYUY,
0484     DRM_FORMAT_YUYV,
0485     DRM_FORMAT_YVYU,
0486     DRM_FORMAT_YUV411,
0487     DRM_FORMAT_YUV420,
0488     DRM_FORMAT_YUV422,
0489     DRM_FORMAT_YVU411,
0490     DRM_FORMAT_YVU420,
0491     DRM_FORMAT_YVU422,
0492 };
0493 
0494 static const u32 sun8i_vi_layer_de3_formats[] = {
0495     DRM_FORMAT_ABGR1555,
0496     DRM_FORMAT_ABGR2101010,
0497     DRM_FORMAT_ABGR4444,
0498     DRM_FORMAT_ABGR8888,
0499     DRM_FORMAT_ARGB1555,
0500     DRM_FORMAT_ARGB2101010,
0501     DRM_FORMAT_ARGB4444,
0502     DRM_FORMAT_ARGB8888,
0503     DRM_FORMAT_BGR565,
0504     DRM_FORMAT_BGR888,
0505     DRM_FORMAT_BGRA1010102,
0506     DRM_FORMAT_BGRA5551,
0507     DRM_FORMAT_BGRA4444,
0508     DRM_FORMAT_BGRA8888,
0509     DRM_FORMAT_BGRX8888,
0510     DRM_FORMAT_RGB565,
0511     DRM_FORMAT_RGB888,
0512     DRM_FORMAT_RGBA1010102,
0513     DRM_FORMAT_RGBA4444,
0514     DRM_FORMAT_RGBA5551,
0515     DRM_FORMAT_RGBA8888,
0516     DRM_FORMAT_RGBX8888,
0517     DRM_FORMAT_XBGR8888,
0518     DRM_FORMAT_XRGB8888,
0519 
0520     DRM_FORMAT_NV16,
0521     DRM_FORMAT_NV12,
0522     DRM_FORMAT_NV21,
0523     DRM_FORMAT_NV61,
0524     DRM_FORMAT_P010,
0525     DRM_FORMAT_P210,
0526     DRM_FORMAT_UYVY,
0527     DRM_FORMAT_VYUY,
0528     DRM_FORMAT_YUYV,
0529     DRM_FORMAT_YVYU,
0530     DRM_FORMAT_YUV411,
0531     DRM_FORMAT_YUV420,
0532     DRM_FORMAT_YUV422,
0533     DRM_FORMAT_YVU411,
0534     DRM_FORMAT_YVU420,
0535     DRM_FORMAT_YVU422,
0536 };
0537 
0538 static const uint64_t sun8i_layer_modifiers[] = {
0539     DRM_FORMAT_MOD_LINEAR,
0540     DRM_FORMAT_MOD_INVALID
0541 };
0542 
0543 struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
0544                            struct sun8i_mixer *mixer,
0545                            int index)
0546 {
0547     enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
0548     u32 supported_encodings, supported_ranges;
0549     unsigned int plane_cnt, format_count;
0550     struct sun8i_vi_layer *layer;
0551     const u32 *formats;
0552     int ret;
0553 
0554     layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
0555     if (!layer)
0556         return ERR_PTR(-ENOMEM);
0557 
0558     if (mixer->cfg->is_de3) {
0559         formats = sun8i_vi_layer_de3_formats;
0560         format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
0561     } else {
0562         formats = sun8i_vi_layer_formats;
0563         format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
0564     }
0565 
0566     if (!mixer->cfg->ui_num && index == 0)
0567         type = DRM_PLANE_TYPE_PRIMARY;
0568 
0569     /* possible crtcs are set later */
0570     ret = drm_universal_plane_init(drm, &layer->plane, 0,
0571                        &sun8i_vi_layer_funcs,
0572                        formats, format_count,
0573                        sun8i_layer_modifiers,
0574                        type, NULL);
0575     if (ret) {
0576         dev_err(drm->dev, "Couldn't initialize layer\n");
0577         return ERR_PTR(ret);
0578     }
0579 
0580     plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
0581 
0582     if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
0583         ret = drm_plane_create_alpha_property(&layer->plane);
0584         if (ret) {
0585             dev_err(drm->dev, "Couldn't add alpha property\n");
0586             return ERR_PTR(ret);
0587         }
0588     }
0589 
0590     ret = drm_plane_create_zpos_property(&layer->plane, index,
0591                          0, plane_cnt - 1);
0592     if (ret) {
0593         dev_err(drm->dev, "Couldn't add zpos property\n");
0594         return ERR_PTR(ret);
0595     }
0596 
0597     supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
0598                   BIT(DRM_COLOR_YCBCR_BT709);
0599     if (mixer->cfg->is_de3)
0600         supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
0601 
0602     supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
0603                BIT(DRM_COLOR_YCBCR_FULL_RANGE);
0604 
0605     ret = drm_plane_create_color_properties(&layer->plane,
0606                         supported_encodings,
0607                         supported_ranges,
0608                         DRM_COLOR_YCBCR_BT709,
0609                         DRM_COLOR_YCBCR_LIMITED_RANGE);
0610     if (ret) {
0611         dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
0612         return ERR_PTR(ret);
0613     }
0614 
0615     drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
0616     layer->mixer = mixer;
0617     layer->channel = index;
0618     layer->overlay = 0;
0619 
0620     return layer;
0621 }