18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2019 NXP. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 78c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "dcss-dev.h" 128c2ecf20Sopenharmony_ci#include "dcss-kms.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int dcss_enable_vblank(struct drm_crtc *crtc) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 178c2ecf20Sopenharmony_ci base); 188c2ecf20Sopenharmony_ci struct dcss_dev *dcss = crtc->dev->dev_private; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci dcss_dtg_vblank_irq_enable(dcss->dtg, true); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci enable_irq(dcss_crtc->irq); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return 0; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void dcss_disable_vblank(struct drm_crtc *crtc) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 328c2ecf20Sopenharmony_ci base); 338c2ecf20Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci disable_irq_nosync(dcss_crtc->irq); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci dcss_dtg_vblank_irq_enable(dcss->dtg, false); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (dcss_crtc->disable_ctxld_kick_irq) 408c2ecf20Sopenharmony_ci dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs dcss_crtc_funcs = { 448c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 458c2ecf20Sopenharmony_ci .destroy = drm_crtc_cleanup, 468c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 478c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 488c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 498c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 508c2ecf20Sopenharmony_ci .enable_vblank = dcss_enable_vblank, 518c2ecf20Sopenharmony_ci .disable_vblank = dcss_disable_vblank, 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_begin(struct drm_crtc *crtc, 558c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_flush(struct drm_crtc *crtc, 618c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 648c2ecf20Sopenharmony_ci base); 658c2ecf20Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 688c2ecf20Sopenharmony_ci if (crtc->state->event) { 698c2ecf20Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc)); 708c2ecf20Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, crtc->state->event); 718c2ecf20Sopenharmony_ci crtc->state->event = NULL; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (dcss_dtg_is_enabled(dcss->dtg)) 768c2ecf20Sopenharmony_ci dcss_ctxld_enable(dcss->ctxld); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_enable(struct drm_crtc *crtc, 808c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 838c2ecf20Sopenharmony_ci base); 848c2ecf20Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 858c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 868c2ecf20Sopenharmony_ci struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode; 878c2ecf20Sopenharmony_ci struct videomode vm; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci drm_display_mode_to_videomode(mode, &vm); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci pm_runtime_get_sync(dcss->dev); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci vm.pixelclock = mode->crtc_clock * 1000; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci dcss_ss_subsam_set(dcss->ss); 968c2ecf20Sopenharmony_ci dcss_dtg_css_set(dcss->dtg); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) { 998c2ecf20Sopenharmony_ci dcss_dtg_sync_set(dcss->dtg, &vm); 1008c2ecf20Sopenharmony_ci dcss_ss_sync_set(dcss->ss, &vm, 1018c2ecf20Sopenharmony_ci mode->flags & DRM_MODE_FLAG_PHSYNC, 1028c2ecf20Sopenharmony_ci mode->flags & DRM_MODE_FLAG_PVSYNC); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci dcss_enable_dtg_and_ss(dcss); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dcss_ctxld_enable(dcss->ctxld); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */ 1108c2ecf20Sopenharmony_ci dcss_crtc->disable_ctxld_kick_irq = true; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_disable(struct drm_crtc *crtc, 1148c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc, 1178c2ecf20Sopenharmony_ci base); 1188c2ecf20Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 1198c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 1208c2ecf20Sopenharmony_ci struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 1258c2ecf20Sopenharmony_ci if (crtc->state->event) { 1268c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 1278c2ecf20Sopenharmony_ci crtc->state->event = NULL; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci reinit_completion(&dcss->disable_completion); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci dcss_disable_dtg_and_ss(dcss); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci dcss_ctxld_enable(dcss->ctxld); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!drm_mode_equal(mode, old_mode) || !crtc->state->active) 1408c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&dcss->disable_completion, 1418c2ecf20Sopenharmony_ci msecs_to_jiffies(100))) 1428c2ecf20Sopenharmony_ci dev_err(dcss->dev, "Shutting off DTG timed out.\n"); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Do not shut off CTXLD kick interrupt when shutting VBLANK off. It 1468c2ecf20Sopenharmony_ci * will be needed to commit the last changes, before going to suspend. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci dcss_crtc->disable_ctxld_kick_irq = false; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dcss->dev); 1538c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dcss->dev); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs dcss_helper_funcs = { 1578c2ecf20Sopenharmony_ci .atomic_begin = dcss_crtc_atomic_begin, 1588c2ecf20Sopenharmony_ci .atomic_flush = dcss_crtc_atomic_flush, 1598c2ecf20Sopenharmony_ci .atomic_enable = dcss_crtc_atomic_enable, 1608c2ecf20Sopenharmony_ci .atomic_disable = dcss_crtc_atomic_disable, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct dcss_crtc *dcss_crtc = dev_id; 1668c2ecf20Sopenharmony_ci struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (!dcss_dtg_vblank_irq_valid(dcss->dtg)) 1698c2ecf20Sopenharmony_ci return IRQ_NONE; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (dcss_ctxld_is_flushed(dcss->ctxld)) 1728c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&dcss_crtc->base); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dcss_dtg_vblank_irq_clear(dcss->dtg); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciint dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct dcss_dev *dcss = drm->dev_private; 1828c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dcss->dev); 1838c2ecf20Sopenharmony_ci int ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base), 1868c2ecf20Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY, 0); 1878c2ecf20Sopenharmony_ci if (IS_ERR(crtc->plane[0])) 1888c2ecf20Sopenharmony_ci return PTR_ERR(crtc->plane[0]); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci crtc->base.port = dcss->of_port; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs); 1938c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base, 1948c2ecf20Sopenharmony_ci NULL, &dcss_crtc_funcs, NULL); 1958c2ecf20Sopenharmony_ci if (ret) { 1968c2ecf20Sopenharmony_ci dev_err(dcss->dev, "failed to init crtc\n"); 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci crtc->irq = platform_get_irq_byname(pdev, "vblank"); 2018c2ecf20Sopenharmony_ci if (crtc->irq < 0) 2028c2ecf20Sopenharmony_ci return crtc->irq; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = request_irq(crtc->irq, dcss_crtc_irq_handler, 2058c2ecf20Sopenharmony_ci 0, "dcss_drm", crtc); 2068c2ecf20Sopenharmony_ci if (ret) { 2078c2ecf20Sopenharmony_ci dev_err(dcss->dev, "irq request failed with %d.\n", ret); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci disable_irq(crtc->irq); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci free_irq(crtc->irq, crtc); 2198c2ecf20Sopenharmony_ci} 220