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