linux/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public License as
   6 * published by the Free Software Foundation; either version 2 of
   7 * the License, or (at your option) any later version.
   8 */
   9
  10#include <drm/drm_atomic.h>
  11#include <drm/drm_atomic_helper.h>
  12#include <drm/drm_crtc.h>
  13#include <drm/drm_crtc_helper.h>
  14#include <drm/drm_fb_cma_helper.h>
  15#include <drm/drm_gem_cma_helper.h>
  16#include <drm/drm_plane_helper.h>
  17#include <drm/drmP.h>
  18
  19#include "sun8i_vi_layer.h"
  20#include "sun8i_mixer.h"
  21#include "sun8i_vi_scaler.h"
  22
  23static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
  24                                  int overlay, bool enable, unsigned int zpos,
  25                                  unsigned int old_zpos)
  26{
  27        u32 val;
  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(channel, 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,
  44                                   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
  45                                   0);
  46
  47                regmap_update_bits(mixer->engine.regs,
  48                                   SUN8I_MIXER_BLEND_ROUTE,
  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, val, val);
  58
  59                val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
  60
  61                regmap_update_bits(mixer->engine.regs,
  62                                   SUN8I_MIXER_BLEND_ROUTE,
  63                                   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
  64                                   val);
  65        }
  66}
  67
  68static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
  69                                       int overlay, struct drm_plane *plane,
  70                                       unsigned int zpos)
  71{
  72        struct drm_plane_state *state = plane->state;
  73        const struct drm_format_info *format = state->fb->format;
  74        u32 src_w, src_h, dst_w, dst_h;
  75        u32 outsize, insize;
  76        u32 hphase, vphase;
  77        bool subsampled;
  78
  79        DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
  80                         channel, overlay);
  81
  82        src_w = drm_rect_width(&state->src) >> 16;
  83        src_h = drm_rect_height(&state->src) >> 16;
  84        dst_w = drm_rect_width(&state->dst);
  85        dst_h = drm_rect_height(&state->dst);
  86
  87        hphase = state->src.x1 & 0xffff;
  88        vphase = state->src.y1 & 0xffff;
  89
  90        /* make coordinates dividable by subsampling factor */
  91        if (format->hsub > 1) {
  92                int mask, remainder;
  93
  94                mask = format->hsub - 1;
  95                remainder = (state->src.x1 >> 16) & mask;
  96                src_w = (src_w + remainder) & ~mask;
  97                hphase += remainder << 16;
  98        }
  99
 100        if (format->vsub > 1) {
 101                int mask, remainder;
 102
 103                mask = format->vsub - 1;
 104                remainder = (state->src.y1 >> 16) & mask;
 105                src_h = (src_h + remainder) & ~mask;
 106                vphase += remainder << 16;
 107        }
 108
 109        insize = SUN8I_MIXER_SIZE(src_w, src_h);
 110        outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 111
 112        /* Set height and width */
 113        DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
 114                         (state->src.x1 >> 16) & ~(format->hsub - 1),
 115                         (state->src.y1 >> 16) & ~(format->vsub - 1));
 116        DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
 117        regmap_write(mixer->engine.regs,
 118                     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay),
 119                     insize);
 120        regmap_write(mixer->engine.regs,
 121                     SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel),
 122                     insize);
 123
 124        /*
 125         * Scaler must be enabled for subsampled formats, so it scales
 126         * chroma to same size as luma.
 127         */
 128        subsampled = format->hsub > 1 || format->vsub > 1;
 129
 130        if (insize != outsize || subsampled || hphase || vphase) {
 131                u32 hscale, vscale;
 132
 133                DRM_DEBUG_DRIVER("HW scaling is enabled\n");
 134
 135                hscale = state->src_w / state->crtc_w;
 136                vscale = state->src_h / state->crtc_h;
 137
 138                sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
 139                                      dst_h, hscale, vscale, hphase, vphase,
 140                                      format);
 141                sun8i_vi_scaler_enable(mixer, channel, true);
 142        } else {
 143                DRM_DEBUG_DRIVER("HW scaling is not needed\n");
 144                sun8i_vi_scaler_enable(mixer, channel, false);
 145        }
 146
 147        /* Set base coordinates */
 148        DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
 149                         state->dst.x1, state->dst.y1);
 150        DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
 151        regmap_write(mixer->engine.regs,
 152                     SUN8I_MIXER_BLEND_ATTR_COORD(zpos),
 153                     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 154        regmap_write(mixer->engine.regs,
 155                     SUN8I_MIXER_BLEND_ATTR_INSIZE(zpos),
 156                     outsize);
 157
 158        return 0;
 159}
 160
 161static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
 162                                         int overlay, struct drm_plane *plane)
 163{
 164        struct drm_plane_state *state = plane->state;
 165        const struct de2_fmt_info *fmt_info;
 166        u32 val;
 167
 168        fmt_info = sun8i_mixer_format_info(state->fb->format->format);
 169        if (!fmt_info) {
 170                DRM_DEBUG_DRIVER("Invalid format\n");
 171                return -EINVAL;
 172        }
 173
 174        val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
 175        regmap_update_bits(mixer->engine.regs,
 176                           SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
 177                           SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
 178
 179        if (fmt_info->csc != SUN8I_CSC_MODE_OFF) {
 180                sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc);
 181                sun8i_csc_enable_ccsc(mixer, channel, true);
 182        } else {
 183                sun8i_csc_enable_ccsc(mixer, channel, false);
 184        }
 185
 186        if (fmt_info->rgb)
 187                val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
 188        else
 189                val = 0;
 190
 191        regmap_update_bits(mixer->engine.regs,
 192                           SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
 193                           SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
 194
 195        return 0;
 196}
 197
 198static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
 199                                        int overlay, struct drm_plane *plane)
 200{
 201        struct drm_plane_state *state = plane->state;
 202        struct drm_framebuffer *fb = state->fb;
 203        const struct drm_format_info *format = fb->format;
 204        struct drm_gem_cma_object *gem;
 205        u32 dx, dy, src_x, src_y;
 206        dma_addr_t paddr;
 207        int i;
 208
 209        /* Adjust x and y to be dividable by subsampling factor */
 210        src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
 211        src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
 212
 213        for (i = 0; i < format->num_planes; i++) {
 214                /* Get the physical address of the buffer in memory */
 215                gem = drm_fb_cma_get_gem_obj(fb, i);
 216
 217                DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
 218
 219                /* Compute the start of the displayed memory */
 220                paddr = gem->paddr + fb->offsets[i];
 221
 222                dx = src_x;
 223                dy = src_y;
 224
 225                if (i > 0) {
 226                        dx /= format->hsub;
 227                        dy /= format->vsub;
 228                }
 229
 230                /* Fixup framebuffer address for src coordinates */
 231                paddr += dx * format->cpp[i];
 232                paddr += dy * fb->pitches[i];
 233
 234                /* Set the line width */
 235                DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
 236                                 i + 1, fb->pitches[i]);
 237                regmap_write(mixer->engine.regs,
 238                             SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel,
 239                                                             overlay, i),
 240               fb->pitches[i]);
 241
 242                DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
 243                                 i + 1, &paddr);
 244
 245                regmap_write(mixer->engine.regs,
 246                             SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel,
 247                                                                 overlay, i),
 248               lower_32_bits(paddr));
 249        }
 250
 251        return 0;
 252}
 253
 254static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
 255                                       struct drm_plane_state *state)
 256{
 257        struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
 258        struct drm_crtc *crtc = state->crtc;
 259        struct drm_crtc_state *crtc_state;
 260        int min_scale, max_scale;
 261
 262        if (!crtc)
 263                return 0;
 264
 265        crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
 266        if (WARN_ON(!crtc_state))
 267                return -EINVAL;
 268
 269        min_scale = DRM_PLANE_HELPER_NO_SCALING;
 270        max_scale = DRM_PLANE_HELPER_NO_SCALING;
 271
 272        if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
 273                min_scale = SUN8I_VI_SCALER_SCALE_MIN;
 274                max_scale = SUN8I_VI_SCALER_SCALE_MAX;
 275        }
 276
 277        return drm_atomic_helper_check_plane_state(state, crtc_state,
 278                                                   min_scale, max_scale,
 279                                                   true, true);
 280}
 281
 282static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
 283                                          struct drm_plane_state *old_state)
 284{
 285        struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
 286        unsigned int old_zpos = old_state->normalized_zpos;
 287        struct sun8i_mixer *mixer = layer->mixer;
 288
 289        sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
 290                              old_zpos);
 291}
 292
 293static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
 294                                         struct drm_plane_state *old_state)
 295{
 296        struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
 297        unsigned int zpos = plane->state->normalized_zpos;
 298        unsigned int old_zpos = old_state->normalized_zpos;
 299        struct sun8i_mixer *mixer = layer->mixer;
 300
 301        if (!plane->state->visible) {
 302                sun8i_vi_layer_enable(mixer, layer->channel,
 303                                      layer->overlay, false, 0, old_zpos);
 304                return;
 305        }
 306
 307        sun8i_vi_layer_update_coord(mixer, layer->channel,
 308                                    layer->overlay, plane, zpos);
 309        sun8i_vi_layer_update_formats(mixer, layer->channel,
 310                                      layer->overlay, plane);
 311        sun8i_vi_layer_update_buffer(mixer, layer->channel,
 312                                     layer->overlay, plane);
 313        sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
 314                              true, zpos, old_zpos);
 315}
 316
 317static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
 318        .atomic_check   = sun8i_vi_layer_atomic_check,
 319        .atomic_disable = sun8i_vi_layer_atomic_disable,
 320        .atomic_update  = sun8i_vi_layer_atomic_update,
 321};
 322
 323static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
 324        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 325        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 326        .destroy                = drm_plane_cleanup,
 327        .disable_plane          = drm_atomic_helper_disable_plane,
 328        .reset                  = drm_atomic_helper_plane_reset,
 329        .update_plane           = drm_atomic_helper_update_plane,
 330};
 331
 332/*
 333 * While all RGB formats are supported, VI planes don't support
 334 * alpha blending, so there is no point having formats with alpha
 335 * channel if their opaque analog exist.
 336 */
 337static const u32 sun8i_vi_layer_formats[] = {
 338        DRM_FORMAT_ABGR1555,
 339        DRM_FORMAT_ABGR4444,
 340        DRM_FORMAT_ARGB1555,
 341        DRM_FORMAT_ARGB4444,
 342        DRM_FORMAT_BGR565,
 343        DRM_FORMAT_BGR888,
 344        DRM_FORMAT_BGRA5551,
 345        DRM_FORMAT_BGRA4444,
 346        DRM_FORMAT_BGRX8888,
 347        DRM_FORMAT_RGB565,
 348        DRM_FORMAT_RGB888,
 349        DRM_FORMAT_RGBA4444,
 350        DRM_FORMAT_RGBA5551,
 351        DRM_FORMAT_RGBX8888,
 352        DRM_FORMAT_XBGR8888,
 353        DRM_FORMAT_XRGB8888,
 354
 355        DRM_FORMAT_NV16,
 356        DRM_FORMAT_NV12,
 357        DRM_FORMAT_NV21,
 358        DRM_FORMAT_NV61,
 359        DRM_FORMAT_UYVY,
 360        DRM_FORMAT_VYUY,
 361        DRM_FORMAT_YUYV,
 362        DRM_FORMAT_YVYU,
 363        DRM_FORMAT_YUV411,
 364        DRM_FORMAT_YUV420,
 365        DRM_FORMAT_YUV422,
 366        DRM_FORMAT_YUV444,
 367        DRM_FORMAT_YVU411,
 368        DRM_FORMAT_YVU420,
 369        DRM_FORMAT_YVU422,
 370        DRM_FORMAT_YVU444,
 371};
 372
 373struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
 374                                               struct sun8i_mixer *mixer,
 375                                               int index)
 376{
 377        struct sun8i_vi_layer *layer;
 378        unsigned int plane_cnt;
 379        int ret;
 380
 381        layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
 382        if (!layer)
 383                return ERR_PTR(-ENOMEM);
 384
 385        /* possible crtcs are set later */
 386        ret = drm_universal_plane_init(drm, &layer->plane, 0,
 387                                       &sun8i_vi_layer_funcs,
 388                                       sun8i_vi_layer_formats,
 389                                       ARRAY_SIZE(sun8i_vi_layer_formats),
 390                                       NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
 391        if (ret) {
 392                dev_err(drm->dev, "Couldn't initialize layer\n");
 393                return ERR_PTR(ret);
 394        }
 395
 396        plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
 397
 398        ret = drm_plane_create_zpos_property(&layer->plane, index,
 399                                             0, plane_cnt - 1);
 400        if (ret) {
 401                dev_err(drm->dev, "Couldn't add zpos property\n");
 402                return ERR_PTR(ret);
 403        }
 404
 405        drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
 406        layer->mixer = mixer;
 407        layer->channel = index;
 408        layer->overlay = 0;
 409
 410        return layer;
 411}
 412