12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21c248b7dSInki Dae /* exynos_drm_crtc.c 31c248b7dSInki Dae * 41c248b7dSInki Dae * Copyright (c) 2011 Samsung Electronics Co., Ltd. 51c248b7dSInki Dae * Authors: 61c248b7dSInki Dae * Inki Dae <inki.dae@samsung.com> 71c248b7dSInki Dae * Joonyoung Shim <jy0922.shim@samsung.com> 81c248b7dSInki Dae * Seung-Woo Kim <sw0312.kim@samsung.com> 91c248b7dSInki Dae */ 101c248b7dSInki Dae 114ea9526bSGustavo Padovan #include <drm/drm_atomic.h> 124ea9526bSGustavo Padovan #include <drm/drm_atomic_helper.h> 131ca582f1SAndrzej Hajda #include <drm/drm_encoder.h> 14fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 152bda34d7SSam Ravnborg #include <drm/drm_vblank.h> 161c248b7dSInki Dae 17e30655d0SMark Brown #include "exynos_drm_crtc.h" 181c248b7dSInki Dae #include "exynos_drm_drv.h" 19b5d2eb3bSJoonyoung Shim #include "exynos_drm_plane.h" 201c248b7dSInki Dae 210b20a0f8SLaurent Pinchart static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc, 22351f950dSMaxime Ripard struct drm_atomic_state *state) 231c248b7dSInki Dae { 24d2716c89SJoonyoung Shim struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 251c248b7dSInki Dae 2611f95489SInki Dae if (exynos_crtc->ops->atomic_enable) 2711f95489SInki Dae exynos_crtc->ops->atomic_enable(exynos_crtc); 28080be03dSSean Paul 29d6948b2fSAndrzej Hajda drm_crtc_vblank_on(crtc); 301c248b7dSInki Dae } 311c248b7dSInki Dae 3264581714SLaurent Pinchart static void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc, 33351f950dSMaxime Ripard struct drm_atomic_state *state) 343fc4867cSGustavo Padovan { 3563498e30SGustavo Padovan struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 363fc4867cSGustavo Padovan 3763498e30SGustavo Padovan drm_crtc_vblank_off(crtc); 3863498e30SGustavo Padovan 3911f95489SInki Dae if (exynos_crtc->ops->atomic_disable) 4011f95489SInki Dae exynos_crtc->ops->atomic_disable(exynos_crtc); 4141cbf0fdSInki Dae 4241cbf0fdSInki Dae if (crtc->state->event && !crtc->state->active) { 4341cbf0fdSInki Dae spin_lock_irq(&crtc->dev->event_lock); 4441cbf0fdSInki Dae drm_crtc_send_vblank_event(crtc, crtc->state->event); 4541cbf0fdSInki Dae spin_unlock_irq(&crtc->dev->event_lock); 4641cbf0fdSInki Dae 4741cbf0fdSInki Dae crtc->state->event = NULL; 4841cbf0fdSInki Dae } 493fc4867cSGustavo Padovan } 503fc4867cSGustavo Padovan 515625b341SAndrzej Hajda static int exynos_crtc_atomic_check(struct drm_crtc *crtc, 5229b77ad7SMaxime Ripard struct drm_atomic_state *state) 535625b341SAndrzej Hajda { 5429b77ad7SMaxime Ripard struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 5529b77ad7SMaxime Ripard crtc); 565625b341SAndrzej Hajda struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 575625b341SAndrzej Hajda 5829b77ad7SMaxime Ripard if (!crtc_state->enable) 59c4e07407SAndrzej Hajda return 0; 60c4e07407SAndrzej Hajda 615625b341SAndrzej Hajda if (exynos_crtc->ops->atomic_check) 6229b77ad7SMaxime Ripard return exynos_crtc->ops->atomic_check(exynos_crtc, crtc_state); 635625b341SAndrzej Hajda 645625b341SAndrzej Hajda return 0; 655625b341SAndrzej Hajda } 665625b341SAndrzej Hajda 67613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, 68*f6ebe9f9SMaxime Ripard struct drm_atomic_state *state) 699d5ab6a0SGustavo Padovan { 709d5ab6a0SGustavo Padovan struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 719d5ab6a0SGustavo Padovan 72d9220d47SGustavo Padovan if (exynos_crtc->ops->atomic_begin) 73d29c2c14SMarek Szyprowski exynos_crtc->ops->atomic_begin(exynos_crtc); 749d5ab6a0SGustavo Padovan } 759d5ab6a0SGustavo Padovan 76613d2b27SMaarten Lankhorst static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, 77*f6ebe9f9SMaxime Ripard struct drm_atomic_state *state) 789d5ab6a0SGustavo Padovan { 79d9220d47SGustavo Padovan struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 80d9220d47SGustavo Padovan 81d9220d47SGustavo Padovan if (exynos_crtc->ops->atomic_flush) 82d29c2c14SMarek Szyprowski exynos_crtc->ops->atomic_flush(exynos_crtc); 839d5ab6a0SGustavo Padovan } 849d5ab6a0SGustavo Padovan 85c3653fedSAndrzej Hajda static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc, 86c3653fedSAndrzej Hajda const struct drm_display_mode *mode) 87c3653fedSAndrzej Hajda { 88c3653fedSAndrzej Hajda struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 89c3653fedSAndrzej Hajda 90c3653fedSAndrzej Hajda if (exynos_crtc->ops->mode_valid) 91c3653fedSAndrzej Hajda return exynos_crtc->ops->mode_valid(exynos_crtc, mode); 92c3653fedSAndrzej Hajda 93c3653fedSAndrzej Hajda return MODE_OK; 94c3653fedSAndrzej Hajda } 95c3653fedSAndrzej Hajda 962466db97SAndrzej Hajda static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc, 972466db97SAndrzej Hajda const struct drm_display_mode *mode, 982466db97SAndrzej Hajda struct drm_display_mode *adjusted_mode) 992466db97SAndrzej Hajda { 1002466db97SAndrzej Hajda struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 1012466db97SAndrzej Hajda 1022466db97SAndrzej Hajda if (exynos_crtc->ops->mode_fixup) 1032466db97SAndrzej Hajda return exynos_crtc->ops->mode_fixup(exynos_crtc, mode, 1042466db97SAndrzej Hajda adjusted_mode); 1052466db97SAndrzej Hajda 1062466db97SAndrzej Hajda return true; 1072466db97SAndrzej Hajda } 1082466db97SAndrzej Hajda 1092466db97SAndrzej Hajda 110800ba2b5SVille Syrjälä static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { 111c3653fedSAndrzej Hajda .mode_valid = exynos_crtc_mode_valid, 1122466db97SAndrzej Hajda .mode_fixup = exynos_crtc_mode_fixup, 1135625b341SAndrzej Hajda .atomic_check = exynos_crtc_atomic_check, 1149d5ab6a0SGustavo Padovan .atomic_begin = exynos_crtc_atomic_begin, 1159d5ab6a0SGustavo Padovan .atomic_flush = exynos_crtc_atomic_flush, 1160b20a0f8SLaurent Pinchart .atomic_enable = exynos_drm_crtc_atomic_enable, 11764581714SLaurent Pinchart .atomic_disable = exynos_drm_crtc_atomic_disable, 1181c248b7dSInki Dae }; 1191c248b7dSInki Dae 120a392276dSAndrzej Hajda void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc) 121a392276dSAndrzej Hajda { 122a392276dSAndrzej Hajda struct drm_crtc *crtc = &exynos_crtc->base; 123a392276dSAndrzej Hajda struct drm_pending_vblank_event *event = crtc->state->event; 124a392276dSAndrzej Hajda unsigned long flags; 125a392276dSAndrzej Hajda 12673b7b44fSAndrzej Hajda if (!event) 12773b7b44fSAndrzej Hajda return; 128a392276dSAndrzej Hajda crtc->state->event = NULL; 129a392276dSAndrzej Hajda 13073b7b44fSAndrzej Hajda WARN_ON(drm_crtc_vblank_get(crtc) != 0); 13173b7b44fSAndrzej Hajda 13273b7b44fSAndrzej Hajda spin_lock_irqsave(&crtc->dev->event_lock, flags); 13373b7b44fSAndrzej Hajda drm_crtc_arm_vblank_event(crtc, event); 13473b7b44fSAndrzej Hajda spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 135a392276dSAndrzej Hajda } 136a392276dSAndrzej Hajda 1371c248b7dSInki Dae static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) 1381c248b7dSInki Dae { 1391c248b7dSInki Dae struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 1401c248b7dSInki Dae 1411c248b7dSInki Dae drm_crtc_cleanup(crtc); 1421c248b7dSInki Dae kfree(exynos_crtc); 1431c248b7dSInki Dae } 1441c248b7dSInki Dae 14564b0e1d6SShawn Guo static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc) 14664b0e1d6SShawn Guo { 14764b0e1d6SShawn Guo struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 14864b0e1d6SShawn Guo 14964b0e1d6SShawn Guo if (exynos_crtc->ops->enable_vblank) 15064b0e1d6SShawn Guo return exynos_crtc->ops->enable_vblank(exynos_crtc); 15164b0e1d6SShawn Guo 15264b0e1d6SShawn Guo return 0; 15364b0e1d6SShawn Guo } 15464b0e1d6SShawn Guo 15564b0e1d6SShawn Guo static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc) 15664b0e1d6SShawn Guo { 15764b0e1d6SShawn Guo struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 15864b0e1d6SShawn Guo 15964b0e1d6SShawn Guo if (exynos_crtc->ops->disable_vblank) 16064b0e1d6SShawn Guo exynos_crtc->ops->disable_vblank(exynos_crtc); 16164b0e1d6SShawn Guo } 16264b0e1d6SShawn Guo 163800ba2b5SVille Syrjälä static const struct drm_crtc_funcs exynos_crtc_funcs = { 16447a7deffSGustavo Padovan .set_config = drm_atomic_helper_set_config, 1659d5ab6a0SGustavo Padovan .page_flip = drm_atomic_helper_page_flip, 1661c248b7dSInki Dae .destroy = exynos_drm_crtc_destroy, 1674ea9526bSGustavo Padovan .reset = drm_atomic_helper_crtc_reset, 1684ea9526bSGustavo Padovan .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 1694ea9526bSGustavo Padovan .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 17064b0e1d6SShawn Guo .enable_vblank = exynos_drm_crtc_enable_vblank, 17164b0e1d6SShawn Guo .disable_vblank = exynos_drm_crtc_disable_vblank, 1721c248b7dSInki Dae }; 1731c248b7dSInki Dae 17493bca243SGustavo Padovan struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, 1757ee14cdcSGustavo Padovan struct drm_plane *plane, 17693bca243SGustavo Padovan enum exynos_drm_output_type type, 177f3aaf762SKrzysztof Kozlowski const struct exynos_drm_crtc_ops *ops, 17893bca243SGustavo Padovan void *ctx) 1791c248b7dSInki Dae { 1801c248b7dSInki Dae struct exynos_drm_crtc *exynos_crtc; 1811c248b7dSInki Dae struct drm_crtc *crtc; 18272ed6ccdSAndrzej Hajda int ret; 1831c248b7dSInki Dae 1841c248b7dSInki Dae exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); 18538bb5253SSachin Kamat if (!exynos_crtc) 18693bca243SGustavo Padovan return ERR_PTR(-ENOMEM); 1871c248b7dSInki Dae 1885d1741adSGustavo Padovan exynos_crtc->type = type; 18993bca243SGustavo Padovan exynos_crtc->ops = ops; 19093bca243SGustavo Padovan exynos_crtc->ctx = ctx; 191b5d2eb3bSJoonyoung Shim 192357193cdSGustavo Padovan crtc = &exynos_crtc->base; 1931c248b7dSInki Dae 194eb88e422SGustavo Padovan ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, 195f9882876SVille Syrjälä &exynos_crtc_funcs, NULL); 19672ed6ccdSAndrzej Hajda if (ret < 0) 19772ed6ccdSAndrzej Hajda goto err_crtc; 19872ed6ccdSAndrzej Hajda 1991c248b7dSInki Dae drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); 2001c248b7dSInki Dae 20193bca243SGustavo Padovan return exynos_crtc; 20272ed6ccdSAndrzej Hajda 20372ed6ccdSAndrzej Hajda err_crtc: 20472ed6ccdSAndrzej Hajda plane->funcs->destroy(plane); 20572ed6ccdSAndrzej Hajda kfree(exynos_crtc); 20693bca243SGustavo Padovan return ERR_PTR(ret); 2071c248b7dSInki Dae } 2081c248b7dSInki Dae 2091ca582f1SAndrzej Hajda struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev, 210cf67cc9aSGustavo Padovan enum exynos_drm_output_type out_type) 211f37cd5e8SInki Dae { 212f37cd5e8SInki Dae struct drm_crtc *crtc; 213f37cd5e8SInki Dae 214d644951cSAndrzej Hajda drm_for_each_crtc(crtc, drm_dev) 215d644951cSAndrzej Hajda if (to_exynos_crtc(crtc)->type == out_type) 2161ca582f1SAndrzej Hajda return to_exynos_crtc(crtc); 217f37cd5e8SInki Dae 218e9497dc2SMarek Szyprowski return ERR_PTR(-ENODEV); 2191ca582f1SAndrzej Hajda } 2201ca582f1SAndrzej Hajda 2211ca582f1SAndrzej Hajda int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder, 2221ca582f1SAndrzej Hajda enum exynos_drm_output_type out_type) 2231ca582f1SAndrzej Hajda { 2241ca582f1SAndrzej Hajda struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev, 2251ca582f1SAndrzej Hajda out_type); 2261ca582f1SAndrzej Hajda 2271ca582f1SAndrzej Hajda if (IS_ERR(crtc)) 2281ca582f1SAndrzej Hajda return PTR_ERR(crtc); 2291ca582f1SAndrzej Hajda 2301ca582f1SAndrzej Hajda encoder->possible_crtcs = drm_crtc_mask(&crtc->base); 2311ca582f1SAndrzej Hajda 2321ca582f1SAndrzej Hajda return 0; 233f37cd5e8SInki Dae } 2345595d4d8SYoungJun Cho 2355595d4d8SYoungJun Cho void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) 2365595d4d8SYoungJun Cho { 23793bca243SGustavo Padovan struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 2385595d4d8SYoungJun Cho 23993bca243SGustavo Padovan if (exynos_crtc->ops->te_handler) 24093bca243SGustavo Padovan exynos_crtc->ops->te_handler(exynos_crtc); 2415595d4d8SYoungJun Cho } 242