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