313 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 Russell King4 * 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 else101 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_plane_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