1 /* exynos_drm_crtc.c 2 * 3 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 4 * Authors: 5 * Inki Dae <inki.dae@samsung.com> 6 * Joonyoung Shim <jy0922.shim@samsung.com> 7 * Seung-Woo Kim <sw0312.kim@samsung.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15 #include <drm/drmP.h> 16 #include <drm/drm_crtc_helper.h> 17 18 #include "exynos_drm_drv.h" 19 #include "exynos_drm_encoder.h" 20 #include "exynos_drm_plane.h" 21 22 #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ 23 drm_crtc) 24 25 enum exynos_crtc_mode { 26 CRTC_MODE_NORMAL, /* normal mode */ 27 CRTC_MODE_BLANK, /* The private plane of crtc is blank */ 28 }; 29 30 /* 31 * Exynos specific crtc structure. 32 * 33 * @drm_crtc: crtc object. 34 * @drm_plane: pointer of private plane object for this crtc 35 * @pipe: a crtc index created at load() with a new crtc object creation 36 * and the crtc object would be set to private->crtc array 37 * to get a crtc object corresponding to this pipe from private->crtc 38 * array when irq interrupt occured. the reason of using this pipe is that 39 * drm framework doesn't support multiple irq yet. 40 * we can refer to the crtc to current hardware interrupt occured through 41 * this pipe value. 42 * @dpms: store the crtc dpms value 43 * @mode: store the crtc mode value 44 */ 45 struct exynos_drm_crtc { 46 struct drm_crtc drm_crtc; 47 struct drm_plane *plane; 48 unsigned int pipe; 49 unsigned int dpms; 50 enum exynos_crtc_mode mode; 51 wait_queue_head_t pending_flip_queue; 52 atomic_t pending_flip; 53 }; 54 55 static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) 56 { 57 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 58 59 DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); 60 61 if (exynos_crtc->dpms == mode) { 62 DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); 63 return; 64 } 65 66 if (mode > DRM_MODE_DPMS_ON) { 67 /* wait for the completion of page flip. */ 68 wait_event(exynos_crtc->pending_flip_queue, 69 atomic_read(&exynos_crtc->pending_flip) == 0); 70 drm_vblank_off(crtc->dev, exynos_crtc->pipe); 71 } 72 73 exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); 74 exynos_crtc->dpms = mode; 75 } 76 77 static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) 78 { 79 DRM_DEBUG_KMS("%s\n", __FILE__); 80 81 /* drm framework doesn't check NULL. */ 82 } 83 84 static void exynos_drm_crtc_commit(struct drm_crtc *crtc) 85 { 86 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 87 88 DRM_DEBUG_KMS("%s\n", __FILE__); 89 90 exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); 91 exynos_plane_commit(exynos_crtc->plane); 92 exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); 93 } 94 95 static bool 96 exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc, 97 const struct drm_display_mode *mode, 98 struct drm_display_mode *adjusted_mode) 99 { 100 DRM_DEBUG_KMS("%s\n", __FILE__); 101 102 /* drm framework doesn't check NULL */ 103 return true; 104 } 105 106 static int 107 exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, 108 struct drm_display_mode *adjusted_mode, int x, int y, 109 struct drm_framebuffer *old_fb) 110 { 111 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 112 struct drm_plane *plane = exynos_crtc->plane; 113 unsigned int crtc_w; 114 unsigned int crtc_h; 115 int pipe = exynos_crtc->pipe; 116 int ret; 117 118 DRM_DEBUG_KMS("%s\n", __FILE__); 119 120 /* 121 * copy the mode data adjusted by mode_fixup() into crtc->mode 122 * so that hardware can be seet to proper mode. 123 */ 124 memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); 125 126 crtc_w = crtc->fb->width - x; 127 crtc_h = crtc->fb->height - y; 128 129 ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, 130 x, y, crtc_w, crtc_h); 131 if (ret) 132 return ret; 133 134 plane->crtc = crtc; 135 plane->fb = crtc->fb; 136 137 exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); 138 139 return 0; 140 } 141 142 static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, 143 struct drm_framebuffer *old_fb) 144 { 145 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 146 struct drm_plane *plane = exynos_crtc->plane; 147 unsigned int crtc_w; 148 unsigned int crtc_h; 149 int ret; 150 151 DRM_DEBUG_KMS("%s\n", __FILE__); 152 153 /* when framebuffer changing is requested, crtc's dpms should be on */ 154 if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) { 155 DRM_ERROR("failed framebuffer changing request.\n"); 156 return -EPERM; 157 } 158 159 crtc_w = crtc->fb->width - x; 160 crtc_h = crtc->fb->height - y; 161 162 ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, 163 x, y, crtc_w, crtc_h); 164 if (ret) 165 return ret; 166 167 exynos_drm_crtc_commit(crtc); 168 169 return 0; 170 } 171 172 static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) 173 { 174 DRM_DEBUG_KMS("%s\n", __FILE__); 175 /* drm framework doesn't check NULL */ 176 } 177 178 static void exynos_drm_crtc_disable(struct drm_crtc *crtc) 179 { 180 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 181 182 DRM_DEBUG_KMS("%s\n", __FILE__); 183 184 exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF); 185 exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 186 } 187 188 static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { 189 .dpms = exynos_drm_crtc_dpms, 190 .prepare = exynos_drm_crtc_prepare, 191 .commit = exynos_drm_crtc_commit, 192 .mode_fixup = exynos_drm_crtc_mode_fixup, 193 .mode_set = exynos_drm_crtc_mode_set, 194 .mode_set_base = exynos_drm_crtc_mode_set_base, 195 .load_lut = exynos_drm_crtc_load_lut, 196 .disable = exynos_drm_crtc_disable, 197 }; 198 199 static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, 200 struct drm_framebuffer *fb, 201 struct drm_pending_vblank_event *event) 202 { 203 struct drm_device *dev = crtc->dev; 204 struct exynos_drm_private *dev_priv = dev->dev_private; 205 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 206 struct drm_framebuffer *old_fb = crtc->fb; 207 int ret = -EINVAL; 208 209 DRM_DEBUG_KMS("%s\n", __FILE__); 210 211 /* when the page flip is requested, crtc's dpms should be on */ 212 if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) { 213 DRM_ERROR("failed page flip request.\n"); 214 return -EINVAL; 215 } 216 217 mutex_lock(&dev->struct_mutex); 218 219 if (event) { 220 /* 221 * the pipe from user always is 0 so we can set pipe number 222 * of current owner to event. 223 */ 224 event->pipe = exynos_crtc->pipe; 225 226 ret = drm_vblank_get(dev, exynos_crtc->pipe); 227 if (ret) { 228 DRM_DEBUG("failed to acquire vblank counter\n"); 229 230 goto out; 231 } 232 233 spin_lock_irq(&dev->event_lock); 234 list_add_tail(&event->base.link, 235 &dev_priv->pageflip_event_list); 236 atomic_set(&exynos_crtc->pending_flip, 1); 237 spin_unlock_irq(&dev->event_lock); 238 239 crtc->fb = fb; 240 ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, 241 NULL); 242 if (ret) { 243 crtc->fb = old_fb; 244 245 spin_lock_irq(&dev->event_lock); 246 drm_vblank_put(dev, exynos_crtc->pipe); 247 list_del(&event->base.link); 248 spin_unlock_irq(&dev->event_lock); 249 250 goto out; 251 } 252 } 253 out: 254 mutex_unlock(&dev->struct_mutex); 255 return ret; 256 } 257 258 static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) 259 { 260 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 261 struct exynos_drm_private *private = crtc->dev->dev_private; 262 263 DRM_DEBUG_KMS("%s\n", __FILE__); 264 265 private->crtc[exynos_crtc->pipe] = NULL; 266 267 drm_crtc_cleanup(crtc); 268 kfree(exynos_crtc); 269 } 270 271 static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, 272 struct drm_property *property, 273 uint64_t val) 274 { 275 struct drm_device *dev = crtc->dev; 276 struct exynos_drm_private *dev_priv = dev->dev_private; 277 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 278 279 DRM_DEBUG_KMS("%s\n", __func__); 280 281 if (property == dev_priv->crtc_mode_property) { 282 enum exynos_crtc_mode mode = val; 283 284 if (mode == exynos_crtc->mode) 285 return 0; 286 287 exynos_crtc->mode = mode; 288 289 switch (mode) { 290 case CRTC_MODE_NORMAL: 291 exynos_drm_crtc_commit(crtc); 292 break; 293 case CRTC_MODE_BLANK: 294 exynos_plane_dpms(exynos_crtc->plane, 295 DRM_MODE_DPMS_OFF); 296 break; 297 default: 298 break; 299 } 300 301 return 0; 302 } 303 304 return -EINVAL; 305 } 306 307 static struct drm_crtc_funcs exynos_crtc_funcs = { 308 .set_config = drm_crtc_helper_set_config, 309 .page_flip = exynos_drm_crtc_page_flip, 310 .destroy = exynos_drm_crtc_destroy, 311 .set_property = exynos_drm_crtc_set_property, 312 }; 313 314 static const struct drm_prop_enum_list mode_names[] = { 315 { CRTC_MODE_NORMAL, "normal" }, 316 { CRTC_MODE_BLANK, "blank" }, 317 }; 318 319 static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) 320 { 321 struct drm_device *dev = crtc->dev; 322 struct exynos_drm_private *dev_priv = dev->dev_private; 323 struct drm_property *prop; 324 325 DRM_DEBUG_KMS("%s\n", __func__); 326 327 prop = dev_priv->crtc_mode_property; 328 if (!prop) { 329 prop = drm_property_create_enum(dev, 0, "mode", mode_names, 330 ARRAY_SIZE(mode_names)); 331 if (!prop) 332 return; 333 334 dev_priv->crtc_mode_property = prop; 335 } 336 337 drm_object_attach_property(&crtc->base, prop, 0); 338 } 339 340 int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) 341 { 342 struct exynos_drm_crtc *exynos_crtc; 343 struct exynos_drm_private *private = dev->dev_private; 344 struct drm_crtc *crtc; 345 346 DRM_DEBUG_KMS("%s\n", __FILE__); 347 348 exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); 349 if (!exynos_crtc) { 350 DRM_ERROR("failed to allocate exynos crtc\n"); 351 return -ENOMEM; 352 } 353 354 exynos_crtc->pipe = nr; 355 exynos_crtc->dpms = DRM_MODE_DPMS_OFF; 356 init_waitqueue_head(&exynos_crtc->pending_flip_queue); 357 atomic_set(&exynos_crtc->pending_flip, 0); 358 exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); 359 if (!exynos_crtc->plane) { 360 kfree(exynos_crtc); 361 return -ENOMEM; 362 } 363 364 crtc = &exynos_crtc->drm_crtc; 365 366 private->crtc[nr] = crtc; 367 368 drm_crtc_init(dev, crtc, &exynos_crtc_funcs); 369 drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); 370 371 exynos_drm_crtc_attach_mode_property(crtc); 372 373 return 0; 374 } 375 376 int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) 377 { 378 struct exynos_drm_private *private = dev->dev_private; 379 struct exynos_drm_crtc *exynos_crtc = 380 to_exynos_crtc(private->crtc[crtc]); 381 382 DRM_DEBUG_KMS("%s\n", __FILE__); 383 384 if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) 385 return -EPERM; 386 387 exynos_drm_fn_encoder(private->crtc[crtc], &crtc, 388 exynos_drm_enable_vblank); 389 390 return 0; 391 } 392 393 void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) 394 { 395 struct exynos_drm_private *private = dev->dev_private; 396 struct exynos_drm_crtc *exynos_crtc = 397 to_exynos_crtc(private->crtc[crtc]); 398 399 DRM_DEBUG_KMS("%s\n", __FILE__); 400 401 if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) 402 return; 403 404 exynos_drm_fn_encoder(private->crtc[crtc], &crtc, 405 exynos_drm_disable_vblank); 406 } 407 408 void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) 409 { 410 struct exynos_drm_private *dev_priv = dev->dev_private; 411 struct drm_pending_vblank_event *e, *t; 412 struct drm_crtc *drm_crtc = dev_priv->crtc[crtc]; 413 struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc); 414 unsigned long flags; 415 416 DRM_DEBUG_KMS("%s\n", __FILE__); 417 418 spin_lock_irqsave(&dev->event_lock, flags); 419 420 list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, 421 base.link) { 422 /* if event's pipe isn't same as crtc then ignore it. */ 423 if (crtc != e->pipe) 424 continue; 425 426 list_del(&e->base.link); 427 drm_send_vblank_event(dev, -1, e); 428 drm_vblank_put(dev, crtc); 429 atomic_set(&exynos_crtc->pending_flip, 0); 430 wake_up(&exynos_crtc->pending_flip_queue); 431 } 432 433 spin_unlock_irqrestore(&dev->event_lock, flags); 434 } 435