linux/drivers/gpu/drm/armada/armada_plane.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 Russell King
   4 *  Rewritten from the dovefb driver, and Armada510 manuals.
   5 */
   6
   7#include <drm/drm_atomic.h>
   8#include <drm/drm_atomic_helper.h>
   9#include <drm/drm_fourcc.h>
  10#include <drm/drm_plane_helper.h>
  11
  12#include "armada_crtc.h"
  13#include "armada_drm.h"
  14#include "armada_fb.h"
  15#include "armada_gem.h"
  16#include "armada_hw.h"
  17#include "armada_plane.h"
  18#include "armada_trace.h"
  19
  20static const uint32_t armada_primary_formats[] = {
  21        DRM_FORMAT_UYVY,
  22        DRM_FORMAT_YUYV,
  23        DRM_FORMAT_VYUY,
  24        DRM_FORMAT_YVYU,
  25        DRM_FORMAT_ARGB8888,
  26        DRM_FORMAT_ABGR8888,
  27        DRM_FORMAT_XRGB8888,
  28        DRM_FORMAT_XBGR8888,
  29        DRM_FORMAT_RGB888,
  30        DRM_FORMAT_BGR888,
  31        DRM_FORMAT_ARGB1555,
  32        DRM_FORMAT_ABGR1555,
  33        DRM_FORMAT_RGB565,
  34        DRM_FORMAT_BGR565,
  35};
  36
  37void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3],
  38        u16 pitches[3], bool interlaced)
  39{
  40        struct drm_framebuffer *fb = state->fb;
  41        const struct drm_format_info *format = fb->format;
  42        unsigned int num_planes = format->num_planes;
  43        unsigned int x = state->src.x1 >> 16;
  44        unsigned int y = state->src.y1 >> 16;
  45        u32 addr = drm_fb_obj(fb)->dev_addr;
  46        int i;
  47
  48        DRM_DEBUG_KMS("pitch %u x %d y %d bpp %d\n",
  49                      fb->pitches[0], x, y, format->cpp[0] * 8);
  50
  51        if (num_planes > 3)
  52                num_planes = 3;
  53
  54        addrs[0][0] = addr + fb->offsets[0] + y * fb->pitches[0] +
  55                      x * format->cpp[0];
  56        pitches[0] = fb->pitches[0];
  57
  58        y /= format->vsub;
  59        x /= format->hsub;
  60
  61        for (i = 1; i < num_planes; i++) {
  62                addrs[0][i] = addr + fb->offsets[i] + y * fb->pitches[i] +
  63                              x * format->cpp[i];
  64                pitches[i] = fb->pitches[i];
  65        }
  66        for (; i < 3; i++) {
  67                addrs[0][i] = 0;
  68                pitches[i] = 0;
  69        }
  70        if (interlaced) {
  71                for (i = 0; i < 3; i++) {
  72                        addrs[1][i] = addrs[0][i] + pitches[i];
  73                        pitches[i] *= 2;
  74                }
  75        } else {
  76                for (i = 0; i < 3; i++)
  77                        addrs[1][i] = addrs[0][i];
  78        }
  79}
  80
  81int armada_drm_plane_atomic_check(struct drm_plane *plane,
  82        struct drm_atomic_state *state)
  83{
  84        struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
  85                                                                                 plane);
  86        struct armada_plane_state *st = to_armada_plane_state(new_plane_state);
  87        struct drm_crtc *crtc = new_plane_state->crtc;
  88        struct drm_crtc_state *crtc_state;
  89        bool interlace;
  90        int ret;
  91
  92        if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc)) {
  93                new_plane_state->visible = false;
  94                return 0;
  95        }
  96
  97        if (state)
  98                crtc_state = drm_atomic_get_existing_crtc_state(state,
  99                                                                crtc);
 100        else
 101                crtc_state = crtc->state;
 102
 103        ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
 104                                                  0,
 105                                                  INT_MAX, true, false);
 106        if (ret)
 107                return ret;
 108
 109        interlace = crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE;
 110        if (interlace) {
 111                if ((new_plane_state->dst.y1 | new_plane_state->dst.y2) & 1)
 112                        return -EINVAL;
 113                st->src_hw = drm_rect_height(&new_plane_state->src) >> 17;
 114                st->dst_yx = new_plane_state->dst.y1 >> 1;
 115                st->dst_hw = drm_rect_height(&new_plane_state->dst) >> 1;
 116        } else {
 117                st->src_hw = drm_rect_height(&new_plane_state->src) >> 16;
 118                st->dst_yx = new_plane_state->dst.y1;
 119                st->dst_hw = drm_rect_height(&new_plane_state->dst);
 120        }
 121
 122        st->src_hw <<= 16;
 123        st->src_hw |= drm_rect_width(&new_plane_state->src) >> 16;
 124        st->dst_yx <<= 16;
 125        st->dst_yx |= new_plane_state->dst.x1 & 0x0000ffff;
 126        st->dst_hw <<= 16;
 127        st->dst_hw |= drm_rect_width(&new_plane_state->dst) & 0x0000ffff;
 128
 129        armada_drm_plane_calc(new_plane_state, st->addrs, st->pitches,
 130                              interlace);
 131        st->interlace = interlace;
 132
 133        return 0;
 134}
 135
 136static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
 137        struct drm_atomic_state *state)
 138{
 139        struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
 140                                                                           plane);
 141        struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
 142                                                                           plane);
 143        struct armada_crtc *dcrtc;
 144        struct armada_regs *regs;
 145        u32 cfg, cfg_mask, val;
 146        unsigned int idx;
 147
 148        DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
 149
 150        if (!new_state->fb || WARN_ON(!new_state->crtc))
 151                return;
 152
 153        DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n",
 154                plane->base.id, plane->name,
 155                new_state->crtc->base.id, new_state->crtc->name,
 156                new_state->fb->base.id,
 157                old_state->visible, new_state->visible);
 158
 159        dcrtc = drm_to_armada_crtc(new_state->crtc);
 160        regs = dcrtc->regs + dcrtc->regs_idx;
 161
 162        idx = 0;
 163        if (!old_state->visible && new_state->visible) {
 164                val = CFG_PDWN64x66;
 165                if (drm_fb_to_armada_fb(new_state->fb)->fmt > CFG_420)
 166                        val |= CFG_PDWN256x24;
 167                armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1);
 168        }
 169        val = armada_src_hw(new_state);
 170        if (armada_src_hw(old_state) != val)
 171                armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN);
 172        val = armada_dst_yx(new_state);
 173        if (armada_dst_yx(old_state) != val)
 174                armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN);
 175        val = armada_dst_hw(new_state);
 176        if (armada_dst_hw(old_state) != val)
 177                armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN);
 178        if (old_state->src.x1 != new_state->src.x1 ||
 179            old_state->src.y1 != new_state->src.y1 ||
 180            old_state->fb != new_state->fb ||
 181            new_state->crtc->state->mode_changed) {
 182                armada_reg_queue_set(regs, idx, armada_addr(new_state, 0, 0),
 183                                     LCD_CFG_GRA_START_ADDR0);
 184                armada_reg_queue_set(regs, idx, armada_addr(new_state, 1, 0),
 185                                     LCD_CFG_GRA_START_ADDR1);
 186                armada_reg_queue_mod(regs, idx, armada_pitch(new_state, 0),
 187                                     0xffff,
 188                                     LCD_CFG_GRA_PITCH);
 189        }
 190        if (old_state->fb != new_state->fb ||
 191            new_state->crtc->state->mode_changed) {
 192                cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(new_state->fb)->fmt) |
 193                      CFG_GRA_MOD(drm_fb_to_armada_fb(new_state->fb)->mod);
 194                if (drm_fb_to_armada_fb(new_state->fb)->fmt > CFG_420)
 195                        cfg |= CFG_PALETTE_ENA;
 196                if (new_state->visible)
 197                        cfg |= CFG_GRA_ENA;
 198                if (to_armada_plane_state(new_state)->interlace)
 199                        cfg |= CFG_GRA_FTOGGLE;
 200                cfg_mask = CFG_GRAFORMAT |
 201                           CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
 202                                       CFG_SWAPYU | CFG_YUV2RGB) |
 203                           CFG_PALETTE_ENA | CFG_GRA_FTOGGLE |
 204                           CFG_GRA_ENA;
 205        } else if (old_state->visible != new_state->visible) {
 206                cfg = new_state->visible ? CFG_GRA_ENA : 0;
 207                cfg_mask = CFG_GRA_ENA;
 208        } else {
 209                cfg = cfg_mask = 0;
 210        }
 211        if (drm_rect_width(&old_state->src) != drm_rect_width(&new_state->src) ||
 212            drm_rect_width(&old_state->dst) != drm_rect_width(&new_state->dst)) {
 213                cfg_mask |= CFG_GRA_HSMOOTH;
 214                if (drm_rect_width(&new_state->src) >> 16 !=
 215                    drm_rect_width(&new_state->dst))
 216                        cfg |= CFG_GRA_HSMOOTH;
 217        }
 218
 219        if (cfg_mask)
 220                armada_reg_queue_mod(regs, idx, cfg, cfg_mask,
 221                                     LCD_SPU_DMA_CTRL0);
 222
 223        dcrtc->regs_idx += idx;
 224}
 225
 226static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane,
 227        struct drm_atomic_state *state)
 228{
 229        struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
 230                                                                           plane);
 231        struct armada_crtc *dcrtc;
 232        struct armada_regs *regs;
 233        unsigned int idx = 0;
 234
 235        DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
 236
 237        if (!old_state->crtc)
 238                return;
 239
 240        DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n",
 241                plane->base.id, plane->name,
 242                old_state->crtc->base.id, old_state->crtc->name,
 243                old_state->fb->base.id);
 244
 245        dcrtc = drm_to_armada_crtc(old_state->crtc);
 246        regs = dcrtc->regs + dcrtc->regs_idx;
 247
 248        /* Disable plane and power down most RAMs and FIFOs */
 249        armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0);
 250        armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 |
 251                             CFG_PDWN32x32 | CFG_PDWN64x66,
 252                             0, LCD_SPU_SRAM_PARA1);
 253
 254        dcrtc->regs_idx += idx;
 255}
 256
 257static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = {
 258        .atomic_check   = armada_drm_plane_atomic_check,
 259        .atomic_update  = armada_drm_primary_plane_atomic_update,
 260        .atomic_disable = armada_drm_primary_plane_atomic_disable,
 261};
 262
 263void armada_plane_reset(struct drm_plane *plane)
 264{
 265        struct armada_plane_state *st;
 266        if (plane->state)
 267                __drm_atomic_helper_plane_destroy_state(plane->state);
 268        kfree(plane->state);
 269        st = kzalloc(sizeof(*st), GFP_KERNEL);
 270        if (st)
 271                __drm_atomic_helper_plane_reset(plane, &st->base);
 272}
 273
 274struct drm_plane_state *armada_plane_duplicate_state(struct drm_plane *plane)
 275{
 276        struct armada_plane_state *st;
 277
 278        if (WARN_ON(!plane->state))
 279                return NULL;
 280
 281        st = kmemdup(plane->state, sizeof(*st), GFP_KERNEL);
 282        if (st)
 283                __drm_atomic_helper_plane_duplicate_state(plane, &st->base);
 284
 285        return &st->base;
 286}
 287
 288static const struct drm_plane_funcs armada_primary_plane_funcs = {
 289        .update_plane   = drm_atomic_helper_update_plane,
 290        .disable_plane  = drm_atomic_helper_disable_plane,
 291        .destroy        = drm_primary_helper_destroy,
 292        .reset          = armada_plane_reset,
 293        .atomic_duplicate_state = armada_plane_duplicate_state,
 294        .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 295};
 296
 297int armada_drm_primary_plane_init(struct drm_device *drm,
 298        struct drm_plane *primary)
 299{
 300        int ret;
 301
 302        drm_plane_helper_add(primary, &armada_primary_plane_helper_funcs);
 303
 304        ret = drm_universal_plane_init(drm, primary, 0,
 305                                       &armada_primary_plane_funcs,
 306                                       armada_primary_formats,
 307                                       ARRAY_SIZE(armada_primary_formats),
 308                                       NULL,
 309                                       DRM_PLANE_TYPE_PRIMARY, NULL);
 310
 311        return ret;
 312}
 313