1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2015 MediaTek Inc. 4 * Author: CK Hu <ck.hu@mediatek.com> 5 */ 6 7 #include <drm/drm_atomic.h> 8 #include <drm/drm_atomic_helper.h> 9 #include <drm/drm_atomic_uapi.h> 10 #include <drm/drm_blend.h> 11 #include <drm/drm_fourcc.h> 12 #include <drm/drm_framebuffer.h> 13 #include <drm/drm_gem_atomic_helper.h> 14 #include <drm/drm_print.h> 15 #include <linux/align.h> 16 17 #include "mtk_crtc.h" 18 #include "mtk_ddp_comp.h" 19 #include "mtk_drm_drv.h" 20 #include "mtk_gem.h" 21 #include "mtk_plane.h" 22 23 static const u64 modifiers[] = { 24 DRM_FORMAT_MOD_LINEAR, 25 DRM_FORMAT_MOD_INVALID, 26 }; 27 28 static void mtk_plane_reset(struct drm_plane *plane) 29 { 30 struct mtk_plane_state *state; 31 32 if (plane->state) { 33 __drm_atomic_helper_plane_destroy_state(plane->state); 34 35 state = to_mtk_plane_state(plane->state); 36 memset(state, 0, sizeof(*state)); 37 } else { 38 state = kzalloc(sizeof(*state), GFP_KERNEL); 39 if (!state) 40 return; 41 } 42 43 __drm_atomic_helper_plane_reset(plane, &state->base); 44 45 state->base.plane = plane; 46 state->pending.format = DRM_FORMAT_RGB565; 47 state->pending.modifier = DRM_FORMAT_MOD_LINEAR; 48 } 49 50 static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane) 51 { 52 struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state); 53 struct mtk_plane_state *state; 54 55 state = kmalloc(sizeof(*state), GFP_KERNEL); 56 if (!state) 57 return NULL; 58 59 __drm_atomic_helper_plane_duplicate_state(plane, &state->base); 60 61 WARN_ON(state->base.plane != plane); 62 63 state->pending = old_state->pending; 64 65 return &state->base; 66 } 67 68 static bool mtk_plane_format_mod_supported(struct drm_plane *plane, 69 uint32_t format, 70 uint64_t modifier) 71 { 72 return modifier == DRM_FORMAT_MOD_LINEAR; 73 } 74 75 static void mtk_plane_destroy_state(struct drm_plane *plane, 76 struct drm_plane_state *state) 77 { 78 __drm_atomic_helper_plane_destroy_state(state); 79 kfree(to_mtk_plane_state(state)); 80 } 81 82 static int mtk_plane_atomic_async_check(struct drm_plane *plane, 83 struct drm_atomic_state *state, bool flip) 84 { 85 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 86 plane); 87 struct drm_crtc_state *crtc_state; 88 int ret; 89 90 if (plane != new_plane_state->crtc->cursor) 91 return -EINVAL; 92 93 if (!plane->state) 94 return -EINVAL; 95 96 if (!plane->state->fb) 97 return -EINVAL; 98 99 ret = mtk_crtc_plane_check(new_plane_state->crtc, plane, 100 to_mtk_plane_state(new_plane_state)); 101 if (ret) 102 return ret; 103 104 crtc_state = drm_atomic_get_new_crtc_state(state, 105 new_plane_state->crtc); 106 107 return drm_atomic_helper_check_plane_state(plane->state, crtc_state, 108 DRM_PLANE_NO_SCALING, 109 DRM_PLANE_NO_SCALING, 110 true, true); 111 } 112 113 static void mtk_plane_update_new_state(struct drm_plane_state *new_state, 114 struct mtk_plane_state *mtk_plane_state) 115 { 116 struct drm_framebuffer *fb = new_state->fb; 117 struct drm_gem_object *gem; 118 struct mtk_gem_obj *mtk_gem; 119 unsigned int pitch, format; 120 u64 modifier; 121 dma_addr_t addr; 122 dma_addr_t hdr_addr = 0; 123 unsigned int hdr_pitch = 0; 124 int offset; 125 126 gem = fb->obj[0]; 127 mtk_gem = to_mtk_gem_obj(gem); 128 addr = mtk_gem->dma_addr; 129 pitch = fb->pitches[0]; 130 format = fb->format->format; 131 modifier = fb->modifier; 132 133 if (modifier == DRM_FORMAT_MOD_LINEAR) { 134 /* 135 * Using dma_addr_t variable to calculate with multiplier of different types, 136 * for example: addr += (new_state->src.x1 >> 16) * fb->format->cpp[0]; 137 * may cause coverity issue with unintentional overflow. 138 */ 139 offset = (new_state->src.x1 >> 16) * fb->format->cpp[0]; 140 addr += offset; 141 offset = (new_state->src.y1 >> 16) * pitch; 142 addr += offset; 143 } else { 144 int width_in_blocks = ALIGN(fb->width, AFBC_DATA_BLOCK_WIDTH) 145 / AFBC_DATA_BLOCK_WIDTH; 146 int height_in_blocks = ALIGN(fb->height, AFBC_DATA_BLOCK_HEIGHT) 147 / AFBC_DATA_BLOCK_HEIGHT; 148 int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH; 149 int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT; 150 int hdr_size, hdr_offset; 151 152 hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE; 153 pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH * 154 AFBC_DATA_BLOCK_HEIGHT * fb->format->cpp[0]; 155 156 hdr_size = ALIGN(hdr_pitch * height_in_blocks, AFBC_HEADER_ALIGNMENT); 157 hdr_offset = hdr_pitch * y_offset_in_blocks + 158 AFBC_HEADER_BLOCK_SIZE * x_offset_in_blocks; 159 160 /* 161 * Using dma_addr_t variable to calculate with multiplier of different types, 162 * for example: addr += hdr_pitch * y_offset_in_blocks; 163 * may cause coverity issue with unintentional overflow. 164 */ 165 hdr_addr = addr + hdr_offset; 166 167 /* The data plane is offset by 1 additional block. */ 168 offset = pitch * y_offset_in_blocks + 169 AFBC_DATA_BLOCK_WIDTH * AFBC_DATA_BLOCK_HEIGHT * 170 fb->format->cpp[0] * (x_offset_in_blocks + 1); 171 172 /* 173 * Using dma_addr_t variable to calculate with multiplier of different types, 174 * for example: addr += pitch * y_offset_in_blocks; 175 * may cause coverity issue with unintentional overflow. 176 */ 177 addr = addr + hdr_size + offset; 178 } 179 180 mtk_plane_state->pending.enable = true; 181 mtk_plane_state->pending.pitch = pitch; 182 mtk_plane_state->pending.hdr_pitch = hdr_pitch; 183 mtk_plane_state->pending.format = format; 184 mtk_plane_state->pending.modifier = modifier; 185 mtk_plane_state->pending.addr = addr; 186 mtk_plane_state->pending.hdr_addr = hdr_addr; 187 mtk_plane_state->pending.x = new_state->dst.x1; 188 mtk_plane_state->pending.y = new_state->dst.y1; 189 mtk_plane_state->pending.width = drm_rect_width(&new_state->dst); 190 mtk_plane_state->pending.height = drm_rect_height(&new_state->dst); 191 mtk_plane_state->pending.rotation = new_state->rotation; 192 mtk_plane_state->pending.color_encoding = new_state->color_encoding; 193 } 194 195 static void mtk_plane_atomic_async_update(struct drm_plane *plane, 196 struct drm_atomic_state *state) 197 { 198 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 199 plane); 200 struct mtk_plane_state *new_plane_state = to_mtk_plane_state(plane->state); 201 202 plane->state->crtc_x = new_state->crtc_x; 203 plane->state->crtc_y = new_state->crtc_y; 204 plane->state->crtc_h = new_state->crtc_h; 205 plane->state->crtc_w = new_state->crtc_w; 206 plane->state->src_x = new_state->src_x; 207 plane->state->src_y = new_state->src_y; 208 plane->state->src_h = new_state->src_h; 209 plane->state->src_w = new_state->src_w; 210 plane->state->dst.x1 = new_state->dst.x1; 211 plane->state->dst.y1 = new_state->dst.y1; 212 213 mtk_plane_update_new_state(new_state, new_plane_state); 214 swap(plane->state->fb, new_state->fb); 215 wmb(); /* Make sure the above parameters are set before update */ 216 new_plane_state->pending.async_dirty = true; 217 mtk_crtc_async_update(new_state->crtc, plane, state); 218 } 219 220 static const struct drm_plane_funcs mtk_plane_funcs = { 221 .update_plane = drm_atomic_helper_update_plane, 222 .disable_plane = drm_atomic_helper_disable_plane, 223 .destroy = drm_plane_cleanup, 224 .reset = mtk_plane_reset, 225 .atomic_duplicate_state = mtk_plane_duplicate_state, 226 .atomic_destroy_state = mtk_plane_destroy_state, 227 .format_mod_supported = mtk_plane_format_mod_supported, 228 }; 229 230 static int mtk_plane_atomic_check(struct drm_plane *plane, 231 struct drm_atomic_state *state) 232 { 233 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 234 plane); 235 struct drm_framebuffer *fb = new_plane_state->fb; 236 struct drm_crtc_state *crtc_state; 237 int ret; 238 239 if (!fb) 240 return 0; 241 242 if (WARN_ON(!new_plane_state->crtc)) 243 return 0; 244 245 ret = mtk_crtc_plane_check(new_plane_state->crtc, plane, 246 to_mtk_plane_state(new_plane_state)); 247 if (ret) 248 return ret; 249 250 crtc_state = drm_atomic_get_crtc_state(state, 251 new_plane_state->crtc); 252 if (IS_ERR(crtc_state)) 253 return PTR_ERR(crtc_state); 254 255 return drm_atomic_helper_check_plane_state(new_plane_state, 256 crtc_state, 257 DRM_PLANE_NO_SCALING, 258 DRM_PLANE_NO_SCALING, 259 true, true); 260 } 261 262 static void mtk_plane_atomic_disable(struct drm_plane *plane, 263 struct drm_atomic_state *state) 264 { 265 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 266 plane); 267 struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state); 268 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 269 plane); 270 271 mtk_plane_state->pending.enable = false; 272 wmb(); /* Make sure the above parameter is set before update */ 273 mtk_plane_state->pending.dirty = true; 274 275 if (old_state && old_state->crtc) 276 mtk_crtc_plane_disable(old_state->crtc, plane); 277 } 278 279 static void mtk_plane_atomic_update(struct drm_plane *plane, 280 struct drm_atomic_state *state) 281 { 282 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 283 plane); 284 struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(new_state); 285 286 if (!new_state->crtc || WARN_ON(!new_state->fb)) 287 return; 288 289 if (!new_state->visible) { 290 mtk_plane_atomic_disable(plane, state); 291 return; 292 } 293 294 mtk_plane_update_new_state(new_state, mtk_plane_state); 295 wmb(); /* Make sure the above parameters are set before update */ 296 mtk_plane_state->pending.dirty = true; 297 } 298 299 static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = { 300 .atomic_check = mtk_plane_atomic_check, 301 .atomic_update = mtk_plane_atomic_update, 302 .atomic_disable = mtk_plane_atomic_disable, 303 .atomic_async_update = mtk_plane_atomic_async_update, 304 .atomic_async_check = mtk_plane_atomic_async_check, 305 }; 306 307 int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane, 308 unsigned long possible_crtcs, enum drm_plane_type type, 309 unsigned int supported_rotations, const u32 blend_modes, 310 const u32 *formats, size_t num_formats, 311 bool supports_afbc, unsigned int plane_idx) 312 { 313 int err; 314 315 if (!formats || !num_formats) { 316 DRM_ERROR("no formats for plane\n"); 317 return -EINVAL; 318 } 319 320 err = drm_universal_plane_init(dev, plane, possible_crtcs, 321 &mtk_plane_funcs, formats, 322 num_formats, 323 supports_afbc ? modifiers : NULL, 324 type, NULL); 325 if (err) { 326 DRM_ERROR("failed to initialize plane\n"); 327 return err; 328 } 329 330 /* 331 * The hardware does not support repositioning planes by muxing: their 332 * Z-position is infact fixed and the only way to change the actual 333 * order is to swap the contents of the entire register set of one 334 * overlay with another, which may be more expensive than desired. 335 * 336 * With no repositioning, the caller of this function guarantees that 337 * the plane_idx is correct. This means that, for example, the PRIMARY 338 * plane fed to this function will always have plane_idx zero. 339 */ 340 err = drm_plane_create_zpos_immutable_property(plane, plane_idx); 341 if (err) { 342 DRM_ERROR("Failed to create zpos property for plane %u\n", plane_idx); 343 return err; 344 } 345 346 if (supported_rotations) { 347 err = drm_plane_create_rotation_property(plane, 348 DRM_MODE_ROTATE_0, 349 supported_rotations); 350 if (err) 351 DRM_INFO("Create rotation property failed\n"); 352 } 353 354 err = drm_plane_create_alpha_property(plane); 355 if (err) 356 DRM_ERROR("failed to create property: alpha\n"); 357 358 if (blend_modes) { 359 err = drm_plane_create_blend_mode_property(plane, blend_modes); 360 if (err) 361 DRM_ERROR("failed to create property: blend_mode\n"); 362 } 363 364 drm_plane_helper_add(plane, &mtk_plane_helper_funcs); 365 366 return 0; 367 } 368