linux/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
   4 *
   5 * Based on sun4i_layer.h, which is:
   6 *   Copyright (C) 2015 Free Electrons
   7 *   Copyright (C) 2015 NextThing Co
   8 *
   9 *   Maxime Ripard <maxime.ripard@free-electrons.com>
  10 */
  11
  12#include <drm/drm_atomic.h>
  13#include <drm/drm_atomic_helper.h>
  14#include <drm/drm_crtc.h>
  15#include <drm/drm_fb_cma_helper.h>
  16#include <drm/drm_fourcc.h>
  17#include <drm/drm_gem_atomic_helper.h>
  18#include <drm/drm_gem_cma_helper.h>
  19#include <drm/drm_plane_helper.h>
  20#include <drm/drm_probe_helper.h>
  21
  22#include "sun8i_mixer.h"
  23#include "sun8i_ui_layer.h"
  24#include "sun8i_ui_scaler.h"
  25
  26static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
  27                                  int overlay, bool enable, unsigned int zpos,
  28                                  unsigned int old_zpos)
  29{
  30        u32 val, bld_base, ch_base;
  31
  32        bld_base = sun8i_blender_base(mixer);
  33        ch_base = sun8i_channel_base(mixer, channel);
  34
  35        DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
  36                         enable ? "En" : "Dis", channel, overlay);
  37
  38        if (enable)
  39                val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
  40        else
  41                val = 0;
  42
  43        regmap_update_bits(mixer->engine.regs,
  44                           SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
  45                           SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
  46
  47        if (!enable || zpos != old_zpos) {
  48                regmap_update_bits(mixer->engine.regs,
  49                                   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
  50                                   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
  51                                   0);
  52
  53                regmap_update_bits(mixer->engine.regs,
  54                                   SUN8I_MIXER_BLEND_ROUTE(bld_base),
  55                                   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
  56                                   0);
  57        }
  58
  59        if (enable) {
  60                val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
  61
  62                regmap_update_bits(mixer->engine.regs,
  63                                   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
  64                                   val, val);
  65
  66                val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
  67
  68                regmap_update_bits(mixer->engine.regs,
  69                                   SUN8I_MIXER_BLEND_ROUTE(bld_base),
  70                                   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
  71                                   val);
  72        }
  73}
  74
  75static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
  76                                        int overlay, struct drm_plane *plane)
  77{
  78        u32 mask, val, ch_base;
  79
  80        ch_base = sun8i_channel_base(mixer, channel);
  81
  82        mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK |
  83                SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK;
  84
  85        val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8);
  86
  87        val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
  88                SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
  89                SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
  90
  91        regmap_update_bits(mixer->engine.regs,
  92                           SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
  93                           mask, val);
  94}
  95
  96static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
  97                                       int overlay, struct drm_plane *plane,
  98                                       unsigned int zpos)
  99{
 100        struct drm_plane_state *state = plane->state;
 101        u32 src_w, src_h, dst_w, dst_h;
 102        u32 bld_base, ch_base;
 103        u32 outsize, insize;
 104        u32 hphase, vphase;
 105
 106        DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
 107                         channel, overlay);
 108
 109        bld_base = sun8i_blender_base(mixer);
 110        ch_base = sun8i_channel_base(mixer, channel);
 111
 112        src_w = drm_rect_width(&state->src) >> 16;
 113        src_h = drm_rect_height(&state->src) >> 16;
 114        dst_w = drm_rect_width(&state->dst);
 115        dst_h = drm_rect_height(&state->dst);
 116
 117        hphase = state->src.x1 & 0xffff;
 118        vphase = state->src.y1 & 0xffff;
 119
 120        insize = SUN8I_MIXER_SIZE(src_w, src_h);
 121        outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
 122
 123        if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
 124                bool interlaced = false;
 125                u32 val;
 126
 127                DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
 128                                 dst_w, dst_h);
 129                regmap_write(mixer->engine.regs,
 130                             SUN8I_MIXER_GLOBAL_SIZE,
 131                             outsize);
 132                regmap_write(mixer->engine.regs,
 133                             SUN8I_MIXER_BLEND_OUTSIZE(bld_base), outsize);
 134
 135                if (state->crtc)
 136                        interlaced = state->crtc->state->adjusted_mode.flags
 137                                & DRM_MODE_FLAG_INTERLACE;
 138
 139                if (interlaced)
 140                        val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
 141                else
 142                        val = 0;
 143
 144                regmap_update_bits(mixer->engine.regs,
 145                                   SUN8I_MIXER_BLEND_OUTCTL(bld_base),
 146                                   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
 147                                   val);
 148
 149                DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
 150                                 interlaced ? "on" : "off");
 151        }
 152
 153        /* Set height and width */
 154        DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
 155                         state->src.x1 >> 16, state->src.y1 >> 16);
 156        DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
 157        regmap_write(mixer->engine.regs,
 158                     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay),
 159                     insize);
 160        regmap_write(mixer->engine.regs,
 161                     SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
 162                     insize);
 163
 164        if (insize != outsize || hphase || vphase) {
 165                u32 hscale, vscale;
 166
 167                DRM_DEBUG_DRIVER("HW scaling is enabled\n");
 168
 169                hscale = state->src_w / state->crtc_w;
 170                vscale = state->src_h / state->crtc_h;
 171
 172                sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w,
 173                                      dst_h, hscale, vscale, hphase, vphase);
 174                sun8i_ui_scaler_enable(mixer, channel, true);
 175        } else {
 176                DRM_DEBUG_DRIVER("HW scaling is not needed\n");
 177                sun8i_ui_scaler_enable(mixer, channel, false);
 178        }
 179
 180        /* Set base coordinates */
 181        DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
 182                         state->dst.x1, state->dst.y1);
 183        DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
 184        regmap_write(mixer->engine.regs,
 185                     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
 186                     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
 187        regmap_write(mixer->engine.regs,
 188                     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
 189                     outsize);
 190
 191        return 0;
 192}
 193
 194static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
 195                                         int overlay, struct drm_plane *plane)
 196{
 197        struct drm_plane_state *state = plane->state;
 198        const struct drm_format_info *fmt;
 199        u32 val, ch_base, hw_fmt;
 200        int ret;
 201
 202        ch_base = sun8i_channel_base(mixer, channel);
 203
 204        fmt = state->fb->format;
 205        ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
 206        if (ret || fmt->is_yuv) {
 207                DRM_DEBUG_DRIVER("Invalid format\n");
 208                return -EINVAL;
 209        }
 210
 211        val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
 212        regmap_update_bits(mixer->engine.regs,
 213                           SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
 214                           SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
 215
 216        return 0;
 217}
 218
 219static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
 220                                        int overlay, struct drm_plane *plane)
 221{
 222        struct drm_plane_state *state = plane->state;
 223        struct drm_framebuffer *fb = state->fb;
 224        struct drm_gem_cma_object *gem;
 225        dma_addr_t paddr;
 226        u32 ch_base;
 227        int bpp;
 228
 229        ch_base = sun8i_channel_base(mixer, channel);
 230
 231        /* Get the physical address of the buffer in memory */
 232        gem = drm_fb_cma_get_gem_obj(fb, 0);
 233
 234        DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
 235
 236        /* Compute the start of the displayed memory */
 237        bpp = fb->format->cpp[0];
 238        paddr = gem->paddr + fb->offsets[0];
 239
 240        /* Fixup framebuffer address for src coordinates */
 241        paddr += (state->src.x1 >> 16) * bpp;
 242        paddr += (state->src.y1 >> 16) * fb->pitches[0];
 243
 244        /* Set the line width */
 245        DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
 246        regmap_write(mixer->engine.regs,
 247                     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay),
 248                     fb->pitches[0]);
 249
 250        DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
 251
 252        regmap_write(mixer->engine.regs,
 253                     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay),
 254                     lower_32_bits(paddr));
 255
 256        return 0;
 257}
 258
 259static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
 260                                       struct drm_atomic_state *state)
 261{
 262        struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
 263                                                                                 plane);
 264        struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
 265        struct drm_crtc *crtc = new_plane_state->crtc;
 266        struct drm_crtc_state *crtc_state;
 267        int min_scale, max_scale;
 268
 269        if (!crtc)
 270                return 0;
 271
 272        crtc_state = drm_atomic_get_existing_crtc_state(state,
 273                                                        crtc);
 274        if (WARN_ON(!crtc_state))
 275                return -EINVAL;
 276
 277        min_scale = DRM_PLANE_HELPER_NO_SCALING;
 278        max_scale = DRM_PLANE_HELPER_NO_SCALING;
 279
 280        if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
 281                min_scale = SUN8I_UI_SCALER_SCALE_MIN;
 282                max_scale = SUN8I_UI_SCALER_SCALE_MAX;
 283        }
 284
 285        return drm_atomic_helper_check_plane_state(new_plane_state,
 286                                                   crtc_state,
 287                                                   min_scale, max_scale,
 288                                                   true, true);
 289}
 290
 291static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
 292                                          struct drm_atomic_state *state)
 293{
 294        struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
 295                                                                           plane);
 296        struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
 297        unsigned int old_zpos = old_state->normalized_zpos;
 298        struct sun8i_mixer *mixer = layer->mixer;
 299
 300        sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
 301                              old_zpos);
 302}
 303
 304static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
 305                                         struct drm_atomic_state *state)
 306{
 307        struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
 308                                                                           plane);
 309        struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
 310                                                                           plane);
 311        struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
 312        unsigned int zpos = new_state->normalized_zpos;
 313        unsigned int old_zpos = old_state->normalized_zpos;
 314        struct sun8i_mixer *mixer = layer->mixer;
 315
 316        if (!new_state->visible) {
 317                sun8i_ui_layer_enable(mixer, layer->channel,
 318                                      layer->overlay, false, 0, old_zpos);
 319                return;
 320        }
 321
 322        sun8i_ui_layer_update_coord(mixer, layer->channel,
 323                                    layer->overlay, plane, zpos);
 324        sun8i_ui_layer_update_alpha(mixer, layer->channel,
 325                                    layer->overlay, plane);
 326        sun8i_ui_layer_update_formats(mixer, layer->channel,
 327                                      layer->overlay, plane);
 328        sun8i_ui_layer_update_buffer(mixer, layer->channel,
 329                                     layer->overlay, plane);
 330        sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay,
 331                              true, zpos, old_zpos);
 332}
 333
 334static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
 335        .atomic_check   = sun8i_ui_layer_atomic_check,
 336        .atomic_disable = sun8i_ui_layer_atomic_disable,
 337        .atomic_update  = sun8i_ui_layer_atomic_update,
 338};
 339
 340static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
 341        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 342        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 343        .destroy                = drm_plane_cleanup,
 344        .disable_plane          = drm_atomic_helper_disable_plane,
 345        .reset                  = drm_atomic_helper_plane_reset,
 346        .update_plane           = drm_atomic_helper_update_plane,
 347};
 348
 349static const u32 sun8i_ui_layer_formats[] = {
 350        DRM_FORMAT_ABGR1555,
 351        DRM_FORMAT_ABGR4444,
 352        DRM_FORMAT_ABGR8888,
 353        DRM_FORMAT_ARGB1555,
 354        DRM_FORMAT_ARGB4444,
 355        DRM_FORMAT_ARGB8888,
 356        DRM_FORMAT_BGR565,
 357        DRM_FORMAT_BGR888,
 358        DRM_FORMAT_BGRA5551,
 359        DRM_FORMAT_BGRA4444,
 360        DRM_FORMAT_BGRA8888,
 361        DRM_FORMAT_BGRX8888,
 362        DRM_FORMAT_RGB565,
 363        DRM_FORMAT_RGB888,
 364        DRM_FORMAT_RGBA4444,
 365        DRM_FORMAT_RGBA5551,
 366        DRM_FORMAT_RGBA8888,
 367        DRM_FORMAT_RGBX8888,
 368        DRM_FORMAT_XBGR8888,
 369        DRM_FORMAT_XRGB8888,
 370};
 371
 372static const uint64_t sun8i_layer_modifiers[] = {
 373        DRM_FORMAT_MOD_LINEAR,
 374        DRM_FORMAT_MOD_INVALID
 375};
 376
 377struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
 378                                               struct sun8i_mixer *mixer,
 379                                               int index)
 380{
 381        enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
 382        int channel = mixer->cfg->vi_num + index;
 383        struct sun8i_ui_layer *layer;
 384        unsigned int plane_cnt;
 385        int ret;
 386
 387        layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
 388        if (!layer)
 389                return ERR_PTR(-ENOMEM);
 390
 391        if (index == 0)
 392                type = DRM_PLANE_TYPE_PRIMARY;
 393
 394        /* possible crtcs are set later */
 395        ret = drm_universal_plane_init(drm, &layer->plane, 0,
 396                                       &sun8i_ui_layer_funcs,
 397                                       sun8i_ui_layer_formats,
 398                                       ARRAY_SIZE(sun8i_ui_layer_formats),
 399                                       sun8i_layer_modifiers, type, NULL);
 400        if (ret) {
 401                dev_err(drm->dev, "Couldn't initialize layer\n");
 402                return ERR_PTR(ret);
 403        }
 404
 405        plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
 406
 407        ret = drm_plane_create_alpha_property(&layer->plane);
 408        if (ret) {
 409                dev_err(drm->dev, "Couldn't add alpha property\n");
 410                return ERR_PTR(ret);
 411        }
 412
 413        ret = drm_plane_create_zpos_property(&layer->plane, channel,
 414                                             0, plane_cnt - 1);
 415        if (ret) {
 416                dev_err(drm->dev, "Couldn't add zpos property\n");
 417                return ERR_PTR(ret);
 418        }
 419
 420        drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
 421        layer->mixer = mixer;
 422        layer->channel = channel;
 423        layer->overlay = 0;
 424
 425        return layer;
 426}
 427