162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2019 NXP. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <drm/drm_atomic.h> 762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 862306a36Sopenharmony_ci#include <drm/drm_vblank.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "dcss-dev.h" 1362306a36Sopenharmony_ci#include "dcss-kms.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int dcss_enable_vblank(struct drm_crtc *crtc) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 1862306a36Sopenharmony_ci base); 1962306a36Sopenharmony_ci struct dcss_dev *dcss = crtc->dev->dev_private; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci dcss_dtg_vblank_irq_enable(dcss->dtg, true); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci enable_irq(dcss_crtc->irq); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return 0; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void dcss_disable_vblank(struct drm_crtc *crtc) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 3362306a36Sopenharmony_ci base); 3462306a36Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci disable_irq_nosync(dcss_crtc->irq); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci dcss_dtg_vblank_irq_enable(dcss->dtg, false); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (dcss_crtc->disable_ctxld_kick_irq) 4162306a36Sopenharmony_ci dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const struct drm_crtc_funcs dcss_crtc_funcs = { 4562306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 4662306a36Sopenharmony_ci .destroy = drm_crtc_cleanup, 4762306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 4862306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 4962306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 5062306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 5162306a36Sopenharmony_ci .enable_vblank = dcss_enable_vblank, 5262306a36Sopenharmony_ci .disable_vblank = dcss_disable_vblank, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void dcss_crtc_atomic_begin(struct drm_crtc *crtc, 5662306a36Sopenharmony_ci struct drm_atomic_state *state) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void dcss_crtc_atomic_flush(struct drm_crtc *crtc, 6262306a36Sopenharmony_ci struct drm_atomic_state *state) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 6562306a36Sopenharmony_ci base); 6662306a36Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 6962306a36Sopenharmony_ci if (crtc->state->event) { 7062306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc)); 7162306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, crtc->state->event); 7262306a36Sopenharmony_ci crtc->state->event = NULL; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (dcss_dtg_is_enabled(dcss->dtg)) 7762306a36Sopenharmony_ci dcss_ctxld_enable(dcss->ctxld); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void dcss_crtc_atomic_enable(struct drm_crtc *crtc, 8162306a36Sopenharmony_ci struct drm_atomic_state *state) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 8462306a36Sopenharmony_ci crtc); 8562306a36Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 8662306a36Sopenharmony_ci base); 8762306a36Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 8862306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 8962306a36Sopenharmony_ci struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode; 9062306a36Sopenharmony_ci struct videomode vm; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci drm_display_mode_to_videomode(mode, &vm); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pm_runtime_get_sync(dcss->dev); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci vm.pixelclock = mode->crtc_clock * 1000; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci dcss_ss_subsam_set(dcss->ss); 9962306a36Sopenharmony_ci dcss_dtg_css_set(dcss->dtg); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) { 10262306a36Sopenharmony_ci dcss_dtg_sync_set(dcss->dtg, &vm); 10362306a36Sopenharmony_ci dcss_ss_sync_set(dcss->ss, &vm, 10462306a36Sopenharmony_ci mode->flags & DRM_MODE_FLAG_PHSYNC, 10562306a36Sopenharmony_ci mode->flags & DRM_MODE_FLAG_PVSYNC); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci dcss_enable_dtg_and_ss(dcss); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci dcss_ctxld_enable(dcss->ctxld); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */ 11362306a36Sopenharmony_ci dcss_crtc->disable_ctxld_kick_irq = true; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void dcss_crtc_atomic_disable(struct drm_crtc *crtc, 11762306a36Sopenharmony_ci struct drm_atomic_state *state) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, 12062306a36Sopenharmony_ci crtc); 12162306a36Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 12262306a36Sopenharmony_ci base); 12362306a36Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 12462306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 12562306a36Sopenharmony_ci struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 13062306a36Sopenharmony_ci if (crtc->state->event) { 13162306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 13262306a36Sopenharmony_ci crtc->state->event = NULL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci reinit_completion(&dcss->disable_completion); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci dcss_disable_dtg_and_ss(dcss); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci dcss_ctxld_enable(dcss->ctxld); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!drm_mode_equal(mode, old_mode) || !crtc->state->active) 14562306a36Sopenharmony_ci if (!wait_for_completion_timeout(&dcss->disable_completion, 14662306a36Sopenharmony_ci msecs_to_jiffies(100))) 14762306a36Sopenharmony_ci dev_err(dcss->dev, "Shutting off DTG timed out.\n"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Do not shut off CTXLD kick interrupt when shutting VBLANK off. It 15162306a36Sopenharmony_ci * will be needed to commit the last changes, before going to suspend. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci dcss_crtc->disable_ctxld_kick_irq = false; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci pm_runtime_mark_last_busy(dcss->dev); 15862306a36Sopenharmony_ci pm_runtime_put_autosuspend(dcss->dev); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs dcss_helper_funcs = { 16262306a36Sopenharmony_ci .atomic_begin = dcss_crtc_atomic_begin, 16362306a36Sopenharmony_ci .atomic_flush = dcss_crtc_atomic_flush, 16462306a36Sopenharmony_ci .atomic_enable = dcss_crtc_atomic_enable, 16562306a36Sopenharmony_ci .atomic_disable = dcss_crtc_atomic_disable, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct dcss_crtc *dcss_crtc = dev_id; 17162306a36Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!dcss_dtg_vblank_irq_valid(dcss->dtg)) 17462306a36Sopenharmony_ci return IRQ_NONE; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (dcss_ctxld_is_flushed(dcss->ctxld)) 17762306a36Sopenharmony_ci drm_crtc_handle_vblank(&dcss_crtc->base); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dcss_dtg_vblank_irq_clear(dcss->dtg); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return IRQ_HANDLED; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciint dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct dcss_dev *dcss = drm->dev_private; 18762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dcss->dev); 18862306a36Sopenharmony_ci int ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base), 19162306a36Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY, 0); 19262306a36Sopenharmony_ci if (IS_ERR(crtc->plane[0])) 19362306a36Sopenharmony_ci return PTR_ERR(crtc->plane[0]); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci crtc->base.port = dcss->of_port; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs); 19862306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base, 19962306a36Sopenharmony_ci NULL, &dcss_crtc_funcs, NULL); 20062306a36Sopenharmony_ci if (ret) { 20162306a36Sopenharmony_ci dev_err(dcss->dev, "failed to init crtc\n"); 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci crtc->irq = platform_get_irq_byname(pdev, "vblank"); 20662306a36Sopenharmony_ci if (crtc->irq < 0) 20762306a36Sopenharmony_ci return crtc->irq; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = request_irq(crtc->irq, dcss_crtc_irq_handler, 21062306a36Sopenharmony_ci 0, "dcss_drm", crtc); 21162306a36Sopenharmony_ci if (ret) { 21262306a36Sopenharmony_ci dev_err(dcss->dev, "irq request failed with %d.\n", ret); 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci disable_irq(crtc->irq); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_civoid dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci free_irq(crtc->irq, crtc); 22462306a36Sopenharmony_ci} 225