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