1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * shmob_drm_plane.c -- SH Mobile DRM Planes 4 * 5 * Copyright (C) 2012 Renesas Electronics Corporation 6 * 7 * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <drm/drm_atomic.h> 11 #include <drm/drm_atomic_helper.h> 12 #include <drm/drm_crtc.h> 13 #include <drm/drm_fb_dma_helper.h> 14 #include <drm/drm_fourcc.h> 15 #include <drm/drm_framebuffer.h> 16 #include <drm/drm_gem_dma_helper.h> 17 #include <drm/drm_plane_helper.h> 18 19 #include "shmob_drm_drv.h" 20 #include "shmob_drm_kms.h" 21 #include "shmob_drm_plane.h" 22 #include "shmob_drm_regs.h" 23 24 struct shmob_drm_plane { 25 struct drm_plane base; 26 unsigned int index; 27 }; 28 29 struct shmob_drm_plane_state { 30 struct drm_plane_state base; 31 32 const struct shmob_drm_format_info *format; 33 u32 dma[2]; 34 }; 35 36 static inline struct shmob_drm_plane *to_shmob_plane(struct drm_plane *plane) 37 { 38 return container_of(plane, struct shmob_drm_plane, base); 39 } 40 41 static inline struct shmob_drm_plane_state *to_shmob_plane_state(struct drm_plane_state *state) 42 { 43 return container_of(state, struct shmob_drm_plane_state, base); 44 } 45 46 static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate) 47 { 48 struct drm_framebuffer *fb = sstate->base.fb; 49 unsigned int x = sstate->base.src_x >> 16; 50 unsigned int y = sstate->base.src_y >> 16; 51 struct drm_gem_dma_object *gem; 52 unsigned int bpp; 53 54 bpp = shmob_drm_format_is_yuv(sstate->format) ? 8 : sstate->format->bpp; 55 gem = drm_fb_dma_get_gem_obj(fb, 0); 56 sstate->dma[0] = gem->dma_addr + fb->offsets[0] 57 + y * fb->pitches[0] + x * bpp / 8; 58 59 if (shmob_drm_format_is_yuv(sstate->format)) { 60 bpp = sstate->format->bpp - 8; 61 gem = drm_fb_dma_get_gem_obj(fb, 1); 62 sstate->dma[1] = gem->dma_addr + fb->offsets[1] 63 + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] 64 + x * (bpp == 16 ? 2 : 1); 65 } 66 } 67 68 static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane, 69 struct drm_plane_state *state) 70 { 71 struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state); 72 struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev); 73 struct drm_framebuffer *fb = state->fb; 74 75 /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */ 76 lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1); 77 lcdc_write(sdev, LDMLSR, fb->pitches[0]); 78 79 /* Word and long word swap. */ 80 lcdc_write(sdev, LDDDSR, sstate->format->ldddsr); 81 82 lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]); 83 if (shmob_drm_format_is_yuv(sstate->format)) 84 lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]); 85 86 lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS); 87 } 88 89 static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane, 90 struct drm_plane_state *state) 91 { 92 struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state); 93 struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev); 94 struct drm_framebuffer *fb = state->fb; 95 u32 format; 96 97 /* TODO: Support ROP3 mode */ 98 format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) | 99 sstate->format->ldbbsifr; 100 101 #define plane_reg_dump(sdev, splane, reg) \ 102 dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ 103 splane->index, #reg, \ 104 lcdc_read(sdev, reg(splane->index)), \ 105 lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) 106 107 plane_reg_dump(sdev, splane, LDBnBSIFR); 108 plane_reg_dump(sdev, splane, LDBnBSSZR); 109 plane_reg_dump(sdev, splane, LDBnBLOCR); 110 plane_reg_dump(sdev, splane, LDBnBSMWR); 111 plane_reg_dump(sdev, splane, LDBnBSAYR); 112 plane_reg_dump(sdev, splane, LDBnBSACR); 113 114 lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); 115 dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, 116 "LDBCR", lcdc_read(sdev, LDBCR)); 117 118 lcdc_write(sdev, LDBnBSIFR(splane->index), format); 119 120 lcdc_write(sdev, LDBnBSSZR(splane->index), 121 (state->crtc_h << LDBBSSZR_BVSS_SHIFT) | 122 (state->crtc_w << LDBBSSZR_BHSS_SHIFT)); 123 lcdc_write(sdev, LDBnBLOCR(splane->index), 124 (state->crtc_y << LDBBLOCR_CVLC_SHIFT) | 125 (state->crtc_x << LDBBLOCR_CHLC_SHIFT)); 126 lcdc_write(sdev, LDBnBSMWR(splane->index), 127 fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); 128 129 lcdc_write(sdev, LDBnBSAYR(splane->index), sstate->dma[0]); 130 if (shmob_drm_format_is_yuv(sstate->format)) 131 lcdc_write(sdev, LDBnBSACR(splane->index), sstate->dma[1]); 132 133 lcdc_write(sdev, LDBCR, 134 LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); 135 dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, 136 "LDBCR", lcdc_read(sdev, LDBCR)); 137 138 plane_reg_dump(sdev, splane, LDBnBSIFR); 139 plane_reg_dump(sdev, splane, LDBnBSSZR); 140 plane_reg_dump(sdev, splane, LDBnBLOCR); 141 plane_reg_dump(sdev, splane, LDBnBSMWR); 142 plane_reg_dump(sdev, splane, LDBnBSAYR); 143 plane_reg_dump(sdev, splane, LDBnBSACR); 144 } 145 146 static int shmob_drm_plane_atomic_check(struct drm_plane *plane, 147 struct drm_atomic_state *state) 148 { 149 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 150 struct shmob_drm_plane_state *sstate = to_shmob_plane_state(new_plane_state); 151 struct drm_crtc_state *crtc_state; 152 bool is_primary = plane->type == DRM_PLANE_TYPE_PRIMARY; 153 int ret; 154 155 if (!new_plane_state->crtc) { 156 /* 157 * The visible field is not reset by the DRM core but only 158 * updated by drm_atomic_helper_check_plane_state(), set it 159 * manually. 160 */ 161 new_plane_state->visible = false; 162 sstate->format = NULL; 163 return 0; 164 } 165 166 crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); 167 if (IS_ERR(crtc_state)) 168 return PTR_ERR(crtc_state); 169 170 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 171 DRM_PLANE_NO_SCALING, 172 DRM_PLANE_NO_SCALING, 173 !is_primary, true); 174 if (ret < 0) 175 return ret; 176 177 if (!new_plane_state->visible) { 178 sstate->format = NULL; 179 return 0; 180 } 181 182 sstate->format = shmob_drm_format_info(new_plane_state->fb->format->format); 183 if (!sstate->format) { 184 dev_dbg(plane->dev->dev, 185 "plane_atomic_check: unsupported format %p4cc\n", 186 &new_plane_state->fb->format->format); 187 return -EINVAL; 188 } 189 190 shmob_drm_plane_compute_base(sstate); 191 192 return 0; 193 } 194 195 static void shmob_drm_plane_atomic_update(struct drm_plane *plane, 196 struct drm_atomic_state *state) 197 { 198 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 199 struct shmob_drm_plane *splane = to_shmob_plane(plane); 200 201 if (!new_plane_state->visible) 202 return; 203 204 if (plane->type == DRM_PLANE_TYPE_PRIMARY) 205 shmob_drm_primary_plane_setup(splane, new_plane_state); 206 else 207 shmob_drm_overlay_plane_setup(splane, new_plane_state); 208 } 209 210 static void shmob_drm_plane_atomic_disable(struct drm_plane *plane, 211 struct drm_atomic_state *state) 212 { 213 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); 214 struct shmob_drm_device *sdev = to_shmob_device(plane->dev); 215 struct shmob_drm_plane *splane = to_shmob_plane(plane); 216 217 if (!old_state->crtc) 218 return; 219 220 if (plane->type != DRM_PLANE_TYPE_OVERLAY) 221 return; 222 223 lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); 224 lcdc_write(sdev, LDBnBSIFR(splane->index), 0); 225 lcdc_write(sdev, LDBCR, 226 LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); 227 } 228 229 static struct drm_plane_state * 230 shmob_drm_plane_atomic_duplicate_state(struct drm_plane *plane) 231 { 232 struct shmob_drm_plane_state *state; 233 struct shmob_drm_plane_state *copy; 234 235 if (WARN_ON(!plane->state)) 236 return NULL; 237 238 state = to_shmob_plane_state(plane->state); 239 copy = kmemdup(state, sizeof(*state), GFP_KERNEL); 240 if (copy == NULL) 241 return NULL; 242 243 __drm_atomic_helper_plane_duplicate_state(plane, ©->base); 244 245 return ©->base; 246 } 247 248 static void shmob_drm_plane_atomic_destroy_state(struct drm_plane *plane, 249 struct drm_plane_state *state) 250 { 251 __drm_atomic_helper_plane_destroy_state(state); 252 kfree(to_shmob_plane_state(state)); 253 } 254 255 static void shmob_drm_plane_reset(struct drm_plane *plane) 256 { 257 struct shmob_drm_plane_state *state; 258 259 if (plane->state) { 260 shmob_drm_plane_atomic_destroy_state(plane, plane->state); 261 plane->state = NULL; 262 } 263 264 state = kzalloc(sizeof(*state), GFP_KERNEL); 265 if (state == NULL) 266 return; 267 268 __drm_atomic_helper_plane_reset(plane, &state->base); 269 } 270 271 static const struct drm_plane_helper_funcs shmob_drm_plane_helper_funcs = { 272 .atomic_check = shmob_drm_plane_atomic_check, 273 .atomic_update = shmob_drm_plane_atomic_update, 274 .atomic_disable = shmob_drm_plane_atomic_disable, 275 }; 276 277 static const struct drm_plane_funcs shmob_drm_plane_funcs = { 278 .update_plane = drm_atomic_helper_update_plane, 279 .disable_plane = drm_atomic_helper_disable_plane, 280 .reset = shmob_drm_plane_reset, 281 .atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state, 282 .atomic_destroy_state = shmob_drm_plane_atomic_destroy_state, 283 }; 284 285 static const uint32_t formats[] = { 286 DRM_FORMAT_RGB565, 287 DRM_FORMAT_RGB888, 288 DRM_FORMAT_ARGB8888, 289 DRM_FORMAT_XRGB8888, 290 DRM_FORMAT_NV12, 291 DRM_FORMAT_NV21, 292 DRM_FORMAT_NV16, 293 DRM_FORMAT_NV61, 294 DRM_FORMAT_NV24, 295 DRM_FORMAT_NV42, 296 }; 297 298 struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev, 299 enum drm_plane_type type, 300 unsigned int index) 301 { 302 struct shmob_drm_plane *splane; 303 304 splane = drmm_universal_plane_alloc(&sdev->ddev, 305 struct shmob_drm_plane, base, 1, 306 &shmob_drm_plane_funcs, formats, 307 ARRAY_SIZE(formats), NULL, type, 308 NULL); 309 if (IS_ERR(splane)) 310 return ERR_CAST(splane); 311 312 splane->index = index; 313 314 drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs); 315 316 return &splane->base; 317 } 318