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 20 static 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 37 void 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 81 int 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 crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 98 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 99 0, 100 INT_MAX, true, false); 101 if (ret) 102 return ret; 103 104 interlace = crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE; 105 if (interlace) { 106 if ((new_plane_state->dst.y1 | new_plane_state->dst.y2) & 1) 107 return -EINVAL; 108 st->src_hw = drm_rect_height(&new_plane_state->src) >> 17; 109 st->dst_yx = new_plane_state->dst.y1 >> 1; 110 st->dst_hw = drm_rect_height(&new_plane_state->dst) >> 1; 111 } else { 112 st->src_hw = drm_rect_height(&new_plane_state->src) >> 16; 113 st->dst_yx = new_plane_state->dst.y1; 114 st->dst_hw = drm_rect_height(&new_plane_state->dst); 115 } 116 117 st->src_hw <<= 16; 118 st->src_hw |= drm_rect_width(&new_plane_state->src) >> 16; 119 st->dst_yx <<= 16; 120 st->dst_yx |= new_plane_state->dst.x1 & 0x0000ffff; 121 st->dst_hw <<= 16; 122 st->dst_hw |= drm_rect_width(&new_plane_state->dst) & 0x0000ffff; 123 124 armada_drm_plane_calc(new_plane_state, st->addrs, st->pitches, 125 interlace); 126 st->interlace = interlace; 127 128 return 0; 129 } 130 131 static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane, 132 struct drm_atomic_state *state) 133 { 134 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 135 plane); 136 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 137 plane); 138 struct armada_crtc *dcrtc; 139 struct armada_regs *regs; 140 u32 cfg, cfg_mask, val; 141 unsigned int idx; 142 143 DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); 144 145 if (!new_state->fb || WARN_ON(!new_state->crtc)) 146 return; 147 148 DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n", 149 plane->base.id, plane->name, 150 new_state->crtc->base.id, new_state->crtc->name, 151 new_state->fb->base.id, 152 old_state->visible, new_state->visible); 153 154 dcrtc = drm_to_armada_crtc(new_state->crtc); 155 regs = dcrtc->regs + dcrtc->regs_idx; 156 157 idx = 0; 158 if (!old_state->visible && new_state->visible) { 159 val = CFG_PDWN64x66; 160 if (drm_fb_to_armada_fb(new_state->fb)->fmt > CFG_420) 161 val |= CFG_PDWN256x24; 162 armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1); 163 } 164 val = armada_src_hw(new_state); 165 if (armada_src_hw(old_state) != val) 166 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN); 167 val = armada_dst_yx(new_state); 168 if (armada_dst_yx(old_state) != val) 169 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN); 170 val = armada_dst_hw(new_state); 171 if (armada_dst_hw(old_state) != val) 172 armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN); 173 if (old_state->src.x1 != new_state->src.x1 || 174 old_state->src.y1 != new_state->src.y1 || 175 old_state->fb != new_state->fb || 176 new_state->crtc->state->mode_changed) { 177 armada_reg_queue_set(regs, idx, armada_addr(new_state, 0, 0), 178 LCD_CFG_GRA_START_ADDR0); 179 armada_reg_queue_set(regs, idx, armada_addr(new_state, 1, 0), 180 LCD_CFG_GRA_START_ADDR1); 181 armada_reg_queue_mod(regs, idx, armada_pitch(new_state, 0), 182 0xffff, 183 LCD_CFG_GRA_PITCH); 184 } 185 if (old_state->fb != new_state->fb || 186 new_state->crtc->state->mode_changed) { 187 cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(new_state->fb)->fmt) | 188 CFG_GRA_MOD(drm_fb_to_armada_fb(new_state->fb)->mod); 189 if (drm_fb_to_armada_fb(new_state->fb)->fmt > CFG_420) 190 cfg |= CFG_PALETTE_ENA; 191 if (new_state->visible) 192 cfg |= CFG_GRA_ENA; 193 if (to_armada_plane_state(new_state)->interlace) 194 cfg |= CFG_GRA_FTOGGLE; 195 cfg_mask = CFG_GRAFORMAT | 196 CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV | 197 CFG_SWAPYU | CFG_YUV2RGB) | 198 CFG_PALETTE_ENA | CFG_GRA_FTOGGLE | 199 CFG_GRA_ENA; 200 } else if (old_state->visible != new_state->visible) { 201 cfg = new_state->visible ? CFG_GRA_ENA : 0; 202 cfg_mask = CFG_GRA_ENA; 203 } else { 204 cfg = cfg_mask = 0; 205 } 206 if (drm_rect_width(&old_state->src) != drm_rect_width(&new_state->src) || 207 drm_rect_width(&old_state->dst) != drm_rect_width(&new_state->dst)) { 208 cfg_mask |= CFG_GRA_HSMOOTH; 209 if (drm_rect_width(&new_state->src) >> 16 != 210 drm_rect_width(&new_state->dst)) 211 cfg |= CFG_GRA_HSMOOTH; 212 } 213 214 if (cfg_mask) 215 armada_reg_queue_mod(regs, idx, cfg, cfg_mask, 216 LCD_SPU_DMA_CTRL0); 217 218 dcrtc->regs_idx += idx; 219 } 220 221 static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane, 222 struct drm_atomic_state *state) 223 { 224 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 225 plane); 226 struct armada_crtc *dcrtc; 227 struct armada_regs *regs; 228 unsigned int idx = 0; 229 230 DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); 231 232 if (!old_state->crtc) 233 return; 234 235 DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n", 236 plane->base.id, plane->name, 237 old_state->crtc->base.id, old_state->crtc->name, 238 old_state->fb->base.id); 239 240 dcrtc = drm_to_armada_crtc(old_state->crtc); 241 regs = dcrtc->regs + dcrtc->regs_idx; 242 243 /* Disable plane and power down most RAMs and FIFOs */ 244 armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0); 245 armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 | 246 CFG_PDWN32x32 | CFG_PDWN64x66, 247 0, LCD_SPU_SRAM_PARA1); 248 249 dcrtc->regs_idx += idx; 250 } 251 252 static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = { 253 .atomic_check = armada_drm_plane_atomic_check, 254 .atomic_update = armada_drm_primary_plane_atomic_update, 255 .atomic_disable = armada_drm_primary_plane_atomic_disable, 256 }; 257 258 void armada_plane_reset(struct drm_plane *plane) 259 { 260 struct armada_plane_state *st; 261 if (plane->state) 262 __drm_atomic_helper_plane_destroy_state(plane->state); 263 kfree(plane->state); 264 st = kzalloc(sizeof(*st), GFP_KERNEL); 265 if (st) 266 __drm_atomic_helper_plane_reset(plane, &st->base); 267 } 268 269 struct drm_plane_state *armada_plane_duplicate_state(struct drm_plane *plane) 270 { 271 struct armada_plane_state *st; 272 273 if (WARN_ON(!plane->state)) 274 return NULL; 275 276 st = kmemdup(plane->state, sizeof(*st), GFP_KERNEL); 277 if (st) 278 __drm_atomic_helper_plane_duplicate_state(plane, &st->base); 279 280 return &st->base; 281 } 282 283 static const struct drm_plane_funcs armada_primary_plane_funcs = { 284 .update_plane = drm_atomic_helper_update_plane, 285 .disable_plane = drm_atomic_helper_disable_plane, 286 .destroy = drm_plane_helper_destroy, 287 .reset = armada_plane_reset, 288 .atomic_duplicate_state = armada_plane_duplicate_state, 289 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 290 }; 291 292 int armada_drm_primary_plane_init(struct drm_device *drm, 293 struct drm_plane *primary) 294 { 295 int ret; 296 297 drm_plane_helper_add(primary, &armada_primary_plane_helper_funcs); 298 299 ret = drm_universal_plane_init(drm, primary, 0, 300 &armada_primary_plane_funcs, 301 armada_primary_formats, 302 ARRAY_SIZE(armada_primary_formats), 303 NULL, 304 DRM_PLANE_TYPE_PRIMARY, NULL); 305 306 return ret; 307 } 308