162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* exynos_drm_crtc.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Inki Dae <inki.dae@samsung.com> 762306a36Sopenharmony_ci * Joonyoung Shim <jy0922.shim@samsung.com> 862306a36Sopenharmony_ci * Seung-Woo Kim <sw0312.kim@samsung.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1262306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1362306a36Sopenharmony_ci#include <drm/drm_encoder.h> 1462306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1562306a36Sopenharmony_ci#include <drm/drm_vblank.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "exynos_drm_crtc.h" 1862306a36Sopenharmony_ci#include "exynos_drm_drv.h" 1962306a36Sopenharmony_ci#include "exynos_drm_plane.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc, 2262306a36Sopenharmony_ci struct drm_atomic_state *state) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (exynos_crtc->ops->atomic_enable) 2762306a36Sopenharmony_ci exynos_crtc->ops->atomic_enable(exynos_crtc); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc, 3362306a36Sopenharmony_ci struct drm_atomic_state *state) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (exynos_crtc->ops->atomic_disable) 4062306a36Sopenharmony_ci exynos_crtc->ops->atomic_disable(exynos_crtc); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 4362306a36Sopenharmony_ci if (crtc->state->event && !crtc->state->active) { 4462306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 4562306a36Sopenharmony_ci crtc->state->event = NULL; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int exynos_crtc_atomic_check(struct drm_crtc *crtc, 5162306a36Sopenharmony_ci struct drm_atomic_state *state) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 5462306a36Sopenharmony_ci crtc); 5562306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (!crtc_state->enable) 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (exynos_crtc->ops->atomic_check) 6162306a36Sopenharmony_ci return exynos_crtc->ops->atomic_check(exynos_crtc, crtc_state); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void exynos_crtc_atomic_begin(struct drm_crtc *crtc, 6762306a36Sopenharmony_ci struct drm_atomic_state *state) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (exynos_crtc->ops->atomic_begin) 7262306a36Sopenharmony_ci exynos_crtc->ops->atomic_begin(exynos_crtc); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void exynos_crtc_atomic_flush(struct drm_crtc *crtc, 7662306a36Sopenharmony_ci struct drm_atomic_state *state) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (exynos_crtc->ops->atomic_flush) 8162306a36Sopenharmony_ci exynos_crtc->ops->atomic_flush(exynos_crtc); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc, 8562306a36Sopenharmony_ci const struct drm_display_mode *mode) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (exynos_crtc->ops->mode_valid) 9062306a36Sopenharmony_ci return exynos_crtc->ops->mode_valid(exynos_crtc, mode); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return MODE_OK; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic bool exynos_crtc_mode_fixup(struct drm_crtc *crtc, 9662306a36Sopenharmony_ci const struct drm_display_mode *mode, 9762306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (exynos_crtc->ops->mode_fixup) 10262306a36Sopenharmony_ci return exynos_crtc->ops->mode_fixup(exynos_crtc, mode, 10362306a36Sopenharmony_ci adjusted_mode); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return true; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { 11062306a36Sopenharmony_ci .mode_valid = exynos_crtc_mode_valid, 11162306a36Sopenharmony_ci .mode_fixup = exynos_crtc_mode_fixup, 11262306a36Sopenharmony_ci .atomic_check = exynos_crtc_atomic_check, 11362306a36Sopenharmony_ci .atomic_begin = exynos_crtc_atomic_begin, 11462306a36Sopenharmony_ci .atomic_flush = exynos_crtc_atomic_flush, 11562306a36Sopenharmony_ci .atomic_enable = exynos_drm_crtc_atomic_enable, 11662306a36Sopenharmony_ci .atomic_disable = exynos_drm_crtc_atomic_disable, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct drm_crtc *crtc = &exynos_crtc->base; 12262306a36Sopenharmony_ci struct drm_pending_vblank_event *event = crtc->state->event; 12362306a36Sopenharmony_ci unsigned long flags; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!event) 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci crtc->state->event = NULL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 13262306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 13362306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void exynos_drm_crtc_destroy(struct drm_crtc *crtc) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 14162306a36Sopenharmony_ci kfree(exynos_crtc); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (exynos_crtc->ops->enable_vblank) 14962306a36Sopenharmony_ci return exynos_crtc->ops->enable_vblank(exynos_crtc); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (exynos_crtc->ops->disable_vblank) 15962306a36Sopenharmony_ci exynos_crtc->ops->disable_vblank(exynos_crtc); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct drm_crtc_funcs exynos_crtc_funcs = { 16362306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 16462306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 16562306a36Sopenharmony_ci .destroy = exynos_drm_crtc_destroy, 16662306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 16762306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 16862306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 16962306a36Sopenharmony_ci .enable_vblank = exynos_drm_crtc_enable_vblank, 17062306a36Sopenharmony_ci .disable_vblank = exynos_drm_crtc_disable_vblank, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistruct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, 17462306a36Sopenharmony_ci struct drm_plane *plane, 17562306a36Sopenharmony_ci enum exynos_drm_output_type type, 17662306a36Sopenharmony_ci const struct exynos_drm_crtc_ops *ops, 17762306a36Sopenharmony_ci void *ctx) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc; 18062306a36Sopenharmony_ci struct drm_crtc *crtc; 18162306a36Sopenharmony_ci int ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); 18462306a36Sopenharmony_ci if (!exynos_crtc) 18562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci exynos_crtc->type = type; 18862306a36Sopenharmony_ci exynos_crtc->ops = ops; 18962306a36Sopenharmony_ci exynos_crtc->ctx = ctx; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci crtc = &exynos_crtc->base; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, 19462306a36Sopenharmony_ci &exynos_crtc_funcs, NULL); 19562306a36Sopenharmony_ci if (ret < 0) 19662306a36Sopenharmony_ci goto err_crtc; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return exynos_crtc; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cierr_crtc: 20362306a36Sopenharmony_ci plane->funcs->destroy(plane); 20462306a36Sopenharmony_ci kfree(exynos_crtc); 20562306a36Sopenharmony_ci return ERR_PTR(ret); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistruct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev, 20962306a36Sopenharmony_ci enum exynos_drm_output_type out_type) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct drm_crtc *crtc; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci drm_for_each_crtc(crtc, drm_dev) 21462306a36Sopenharmony_ci if (to_exynos_crtc(crtc)->type == out_type) 21562306a36Sopenharmony_ci return to_exynos_crtc(crtc); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciint exynos_drm_set_possible_crtcs(struct drm_encoder *encoder, 22162306a36Sopenharmony_ci enum exynos_drm_output_type out_type) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev, 22462306a36Sopenharmony_ci out_type); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (IS_ERR(crtc)) 22762306a36Sopenharmony_ci return PTR_ERR(crtc); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci encoder->possible_crtcs = drm_crtc_mask(&crtc->base); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid exynos_drm_crtc_te_handler(struct drm_crtc *crtc) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (exynos_crtc->ops->te_handler) 23962306a36Sopenharmony_ci exynos_crtc->ops->te_handler(exynos_crtc); 24062306a36Sopenharmony_ci} 241