1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2011 Samsung Electronics Co.Ltd 4 * Authors: Joonyoung Shim <jy0922.shim@samsung.com> 5 */ 6 7 #include <drm/drmP.h> 8 9 #include <drm/drm_atomic.h> 10 #include <drm/drm_atomic_helper.h> 11 #include <drm/drm_plane_helper.h> 12 #include <drm/exynos_drm.h> 13 #include "exynos_drm_drv.h" 14 #include "exynos_drm_crtc.h" 15 #include "exynos_drm_fb.h" 16 #include "exynos_drm_gem.h" 17 #include "exynos_drm_plane.h" 18 19 /* 20 * This function is to get X or Y size shown via screen. This needs length and 21 * start position of CRTC. 22 * 23 * <--- length ---> 24 * CRTC ---------------- 25 * ^ start ^ end 26 * 27 * There are six cases from a to f. 28 * 29 * <----- SCREEN -----> 30 * 0 last 31 * ----------|------------------|---------- 32 * CRTCs 33 * a ------- 34 * b ------- 35 * c -------------------------- 36 * d -------- 37 * e ------- 38 * f ------- 39 */ 40 static int exynos_plane_get_size(int start, unsigned length, unsigned last) 41 { 42 int end = start + length; 43 int size = 0; 44 45 if (start <= 0) { 46 if (end > 0) 47 size = min_t(unsigned, end, last); 48 } else if (start <= last) { 49 size = min_t(unsigned, last - start, length); 50 } 51 52 return size; 53 } 54 55 static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state) 56 { 57 struct drm_plane_state *state = &exynos_state->base; 58 struct drm_crtc *crtc = state->crtc; 59 struct drm_crtc_state *crtc_state = 60 drm_atomic_get_existing_crtc_state(state->state, crtc); 61 struct drm_display_mode *mode = &crtc_state->adjusted_mode; 62 int crtc_x, crtc_y; 63 unsigned int crtc_w, crtc_h; 64 unsigned int src_x, src_y; 65 unsigned int src_w, src_h; 66 unsigned int actual_w; 67 unsigned int actual_h; 68 69 /* 70 * The original src/dest coordinates are stored in exynos_state->base, 71 * but we want to keep another copy internal to our driver that we can 72 * clip/modify ourselves. 73 */ 74 75 crtc_x = state->crtc_x; 76 crtc_y = state->crtc_y; 77 crtc_w = state->crtc_w; 78 crtc_h = state->crtc_h; 79 80 src_x = state->src_x >> 16; 81 src_y = state->src_y >> 16; 82 src_w = state->src_w >> 16; 83 src_h = state->src_h >> 16; 84 85 /* set ratio */ 86 exynos_state->h_ratio = (src_w << 16) / crtc_w; 87 exynos_state->v_ratio = (src_h << 16) / crtc_h; 88 89 /* clip to visible area */ 90 actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay); 91 actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay); 92 93 if (crtc_x < 0) { 94 if (actual_w) 95 src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16; 96 crtc_x = 0; 97 } 98 99 if (crtc_y < 0) { 100 if (actual_h) 101 src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16; 102 crtc_y = 0; 103 } 104 105 /* set drm framebuffer data. */ 106 exynos_state->src.x = src_x; 107 exynos_state->src.y = src_y; 108 exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16; 109 exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16; 110 111 /* set plane range to be displayed. */ 112 exynos_state->crtc.x = crtc_x; 113 exynos_state->crtc.y = crtc_y; 114 exynos_state->crtc.w = actual_w; 115 exynos_state->crtc.h = actual_h; 116 117 DRM_DEV_DEBUG_KMS(crtc->dev->dev, 118 "plane : offset_x/y(%d,%d), width/height(%d,%d)", 119 exynos_state->crtc.x, exynos_state->crtc.y, 120 exynos_state->crtc.w, exynos_state->crtc.h); 121 } 122 123 static void exynos_drm_plane_reset(struct drm_plane *plane) 124 { 125 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 126 struct exynos_drm_plane_state *exynos_state; 127 128 if (plane->state) { 129 exynos_state = to_exynos_plane_state(plane->state); 130 __drm_atomic_helper_plane_destroy_state(plane->state); 131 kfree(exynos_state); 132 plane->state = NULL; 133 } 134 135 exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL); 136 if (exynos_state) { 137 __drm_atomic_helper_plane_reset(plane, &exynos_state->base); 138 plane->state->zpos = exynos_plane->config->zpos; 139 } 140 } 141 142 static struct drm_plane_state * 143 exynos_drm_plane_duplicate_state(struct drm_plane *plane) 144 { 145 struct exynos_drm_plane_state *exynos_state; 146 struct exynos_drm_plane_state *copy; 147 148 exynos_state = to_exynos_plane_state(plane->state); 149 copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL); 150 if (!copy) 151 return NULL; 152 153 __drm_atomic_helper_plane_duplicate_state(plane, ©->base); 154 return ©->base; 155 } 156 157 static void exynos_drm_plane_destroy_state(struct drm_plane *plane, 158 struct drm_plane_state *old_state) 159 { 160 struct exynos_drm_plane_state *old_exynos_state = 161 to_exynos_plane_state(old_state); 162 __drm_atomic_helper_plane_destroy_state(old_state); 163 kfree(old_exynos_state); 164 } 165 166 static struct drm_plane_funcs exynos_plane_funcs = { 167 .update_plane = drm_atomic_helper_update_plane, 168 .disable_plane = drm_atomic_helper_disable_plane, 169 .destroy = drm_plane_cleanup, 170 .reset = exynos_drm_plane_reset, 171 .atomic_duplicate_state = exynos_drm_plane_duplicate_state, 172 .atomic_destroy_state = exynos_drm_plane_destroy_state, 173 }; 174 175 static int 176 exynos_drm_plane_check_format(const struct exynos_drm_plane_config *config, 177 struct exynos_drm_plane_state *state) 178 { 179 struct drm_framebuffer *fb = state->base.fb; 180 struct drm_device *dev = fb->dev; 181 182 switch (fb->modifier) { 183 case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 184 if (!(config->capabilities & EXYNOS_DRM_PLANE_CAP_TILE)) 185 return -ENOTSUPP; 186 break; 187 188 case DRM_FORMAT_MOD_LINEAR: 189 break; 190 191 default: 192 DRM_DEV_ERROR(dev->dev, "unsupported pixel format modifier"); 193 return -ENOTSUPP; 194 } 195 196 return 0; 197 } 198 199 static int 200 exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config, 201 struct exynos_drm_plane_state *state) 202 { 203 struct drm_crtc *crtc = state->base.crtc; 204 bool width_ok = false, height_ok = false; 205 206 if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE) 207 return 0; 208 209 if (state->src.w == state->crtc.w) 210 width_ok = true; 211 212 if (state->src.h == state->crtc.h) 213 height_ok = true; 214 215 if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && 216 state->h_ratio == (1 << 15)) 217 width_ok = true; 218 219 if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && 220 state->v_ratio == (1 << 15)) 221 height_ok = true; 222 223 if (width_ok && height_ok) 224 return 0; 225 226 DRM_DEV_DEBUG_KMS(crtc->dev->dev, "scaling mode is not supported"); 227 return -ENOTSUPP; 228 } 229 230 static int exynos_plane_atomic_check(struct drm_plane *plane, 231 struct drm_plane_state *state) 232 { 233 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 234 struct exynos_drm_plane_state *exynos_state = 235 to_exynos_plane_state(state); 236 int ret = 0; 237 238 if (!state->crtc || !state->fb) 239 return 0; 240 241 /* translate state into exynos_state */ 242 exynos_plane_mode_set(exynos_state); 243 244 ret = exynos_drm_plane_check_format(exynos_plane->config, exynos_state); 245 if (ret) 246 return ret; 247 248 ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state); 249 return ret; 250 } 251 252 static void exynos_plane_atomic_update(struct drm_plane *plane, 253 struct drm_plane_state *old_state) 254 { 255 struct drm_plane_state *state = plane->state; 256 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc); 257 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 258 259 if (!state->crtc) 260 return; 261 262 if (exynos_crtc->ops->update_plane) 263 exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); 264 } 265 266 static void exynos_plane_atomic_disable(struct drm_plane *plane, 267 struct drm_plane_state *old_state) 268 { 269 struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 270 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(old_state->crtc); 271 272 if (!old_state->crtc) 273 return; 274 275 if (exynos_crtc->ops->disable_plane) 276 exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane); 277 } 278 279 static const struct drm_plane_helper_funcs plane_helper_funcs = { 280 .atomic_check = exynos_plane_atomic_check, 281 .atomic_update = exynos_plane_atomic_update, 282 .atomic_disable = exynos_plane_atomic_disable, 283 }; 284 285 static void exynos_plane_attach_zpos_property(struct drm_plane *plane, 286 int zpos, bool immutable) 287 { 288 if (immutable) 289 drm_plane_create_zpos_immutable_property(plane, zpos); 290 else 291 drm_plane_create_zpos_property(plane, zpos, 0, MAX_PLANE - 1); 292 } 293 294 int exynos_plane_init(struct drm_device *dev, 295 struct exynos_drm_plane *exynos_plane, unsigned int index, 296 const struct exynos_drm_plane_config *config) 297 { 298 int err; 299 unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) | 300 BIT(DRM_MODE_BLEND_PREMULTI) | 301 BIT(DRM_MODE_BLEND_COVERAGE); 302 struct drm_plane *plane = &exynos_plane->base; 303 304 err = drm_universal_plane_init(dev, &exynos_plane->base, 305 1 << dev->mode_config.num_crtc, 306 &exynos_plane_funcs, 307 config->pixel_formats, 308 config->num_pixel_formats, 309 NULL, config->type, NULL); 310 if (err) { 311 DRM_DEV_ERROR(dev->dev, "failed to initialize plane\n"); 312 return err; 313 } 314 315 drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs); 316 317 exynos_plane->index = index; 318 exynos_plane->config = config; 319 320 exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos, 321 !(config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS)); 322 323 if (config->capabilities & EXYNOS_DRM_PLANE_CAP_PIX_BLEND) 324 drm_plane_create_blend_mode_property(plane, supported_modes); 325 326 if (config->capabilities & EXYNOS_DRM_PLANE_CAP_WIN_BLEND) 327 drm_plane_create_alpha_property(plane); 328 329 return 0; 330 } 331