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