18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ 48c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 88c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "tidss_crtc.h" 178c2ecf20Sopenharmony_ci#include "tidss_dispc.h" 188c2ecf20Sopenharmony_ci#include "tidss_drv.h" 198c2ecf20Sopenharmony_ci#include "tidss_irq.h" 208c2ecf20Sopenharmony_ci#include "tidss_plane.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Page flip and frame done IRQs */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct drm_device *ddev = tcrtc->crtc.dev; 278c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 288c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event; 298c2ecf20Sopenharmony_ci unsigned long flags; 308c2ecf20Sopenharmony_ci bool busy; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci spin_lock_irqsave(&ddev->event_lock, flags); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* 358c2ecf20Sopenharmony_ci * New settings are taken into use at VFP, and GO bit is cleared at 368c2ecf20Sopenharmony_ci * the same time. This happens before the vertical blank interrupt. 378c2ecf20Sopenharmony_ci * So there is a small change that the driver sets GO bit after VFP, but 388c2ecf20Sopenharmony_ci * before vblank, and we have to check for that case here. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci busy = dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport); 418c2ecf20Sopenharmony_ci if (busy) { 428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ddev->event_lock, flags); 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci event = tcrtc->event; 478c2ecf20Sopenharmony_ci tcrtc->event = NULL; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (!event) { 508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ddev->event_lock, flags); 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(&tcrtc->crtc, event); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ddev->event_lock, flags); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci drm_crtc_vblank_put(&tcrtc->crtc); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_civoid tidss_crtc_vblank_irq(struct drm_crtc *crtc) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(crtc); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci tidss_crtc_finish_page_flip(tcrtc); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_civoid tidss_crtc_framedone_irq(struct drm_crtc *crtc) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci complete(&tcrtc->framedone_completion); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_civoid tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n", 828c2ecf20Sopenharmony_ci tcrtc->hw_videoport, irqstatus); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* drm_crtc_helper_funcs */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int tidss_crtc_atomic_check(struct drm_crtc *crtc, 888c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 918c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 928c2ecf20Sopenharmony_ci struct dispc_device *dispc = tidss->dispc; 938c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 948c2ecf20Sopenharmony_ci u32 hw_videoport = tcrtc->hw_videoport; 958c2ecf20Sopenharmony_ci const struct drm_display_mode *mode; 968c2ecf20Sopenharmony_ci enum drm_mode_status ok; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s\n", __func__); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!state->enable) 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mode = &state->adjusted_mode; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ok = dispc_vp_mode_valid(dispc, hw_videoport, mode); 1068c2ecf20Sopenharmony_ci if (ok != MODE_OK) { 1078c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n", 1088c2ecf20Sopenharmony_ci __func__, mode->hdisplay, mode->vdisplay, mode->clock); 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return dispc_vp_bus_check(dispc, hw_videoport, state); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * This needs all affected planes to be present in the atomic 1178c2ecf20Sopenharmony_ci * state. The untouched planes are added to the state in 1188c2ecf20Sopenharmony_ci * tidss_atomic_check(). 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic void tidss_crtc_position_planes(struct tidss_device *tidss, 1218c2ecf20Sopenharmony_ci struct drm_crtc *crtc, 1228c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state, 1238c2ecf20Sopenharmony_ci bool newmodeset) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct drm_atomic_state *ostate = old_state->state; 1268c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 1278c2ecf20Sopenharmony_ci struct drm_crtc_state *cstate = crtc->state; 1288c2ecf20Sopenharmony_ci int layer; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!newmodeset && !cstate->zpos_changed && 1318c2ecf20Sopenharmony_ci !to_tidss_crtc_state(cstate)->plane_pos_changed) 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for (layer = 0; layer < tidss->feat->num_planes; layer++) { 1358c2ecf20Sopenharmony_ci struct drm_plane_state *pstate; 1368c2ecf20Sopenharmony_ci struct drm_plane *plane; 1378c2ecf20Sopenharmony_ci bool layer_active = false; 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for_each_new_plane_in_state(ostate, plane, pstate, i) { 1418c2ecf20Sopenharmony_ci if (pstate->crtc != crtc || !pstate->visible) 1428c2ecf20Sopenharmony_ci continue; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (pstate->normalized_zpos == layer) { 1458c2ecf20Sopenharmony_ci layer_active = true; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (layer_active) { 1518c2ecf20Sopenharmony_ci struct tidss_plane *tplane = to_tidss_plane(plane); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci dispc_ovr_set_plane(tidss->dispc, tplane->hw_plane_id, 1548c2ecf20Sopenharmony_ci tcrtc->hw_videoport, 1558c2ecf20Sopenharmony_ci pstate->crtc_x, pstate->crtc_y, 1568c2ecf20Sopenharmony_ci layer); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer, 1598c2ecf20Sopenharmony_ci layer_active); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void tidss_crtc_atomic_flush(struct drm_crtc *crtc, 1648c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 1678c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 1688c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 1698c2ecf20Sopenharmony_ci unsigned long flags; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s: %s is %sactive, %s modeset, event %p\n", 1728c2ecf20Sopenharmony_ci __func__, crtc->name, crtc->state->active ? "" : "not ", 1738c2ecf20Sopenharmony_ci drm_atomic_crtc_needs_modeset(crtc->state) ? "needs" : "doesn't need", 1748c2ecf20Sopenharmony_ci crtc->state->event); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* There is nothing to do if CRTC is not going to be enabled. */ 1778c2ecf20Sopenharmony_ci if (!crtc->state->active) 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* 1818c2ecf20Sopenharmony_ci * Flush CRTC changes with go bit only if new modeset is not 1828c2ecf20Sopenharmony_ci * coming, so CRTC is enabled trough out the commit. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(crtc->state)) 1858c2ecf20Sopenharmony_ci return; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* If the GO bit is stuck we better quit here. */ 1888c2ecf20Sopenharmony_ci if (WARN_ON(dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport))) 1898c2ecf20Sopenharmony_ci return; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* We should have event if CRTC is enabled through out this commit. */ 1928c2ecf20Sopenharmony_ci if (WARN_ON(!crtc->state->event)) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Write vp properties to HW if needed. */ 1968c2ecf20Sopenharmony_ci dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, false); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Update plane positions if needed. */ 1998c2ecf20Sopenharmony_ci tidss_crtc_position_planes(tidss, crtc, old_crtc_state, false); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci spin_lock_irqsave(&ddev->event_lock, flags); 2048c2ecf20Sopenharmony_ci dispc_vp_go(tidss->dispc, tcrtc->hw_videoport); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci WARN_ON(tcrtc->event); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci tcrtc->event = crtc->state->event; 2098c2ecf20Sopenharmony_ci crtc->state->event = NULL; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ddev->event_lock, flags); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void tidss_crtc_atomic_enable(struct drm_crtc *crtc, 2158c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 2188c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 2198c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 2208c2ecf20Sopenharmony_ci const struct drm_display_mode *mode = &crtc->state->adjusted_mode; 2218c2ecf20Sopenharmony_ci unsigned long flags; 2228c2ecf20Sopenharmony_ci int r; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci tidss_runtime_get(tidss); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport, 2298c2ecf20Sopenharmony_ci mode->clock * 1000); 2308c2ecf20Sopenharmony_ci if (r != 0) 2318c2ecf20Sopenharmony_ci return; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport); 2348c2ecf20Sopenharmony_ci if (r != 0) 2358c2ecf20Sopenharmony_ci return; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true); 2388c2ecf20Sopenharmony_ci tidss_crtc_position_planes(tidss, crtc, old_state, true); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Turn vertical blanking interrupt reporting on. */ 2418c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci spin_lock_irqsave(&ddev->event_lock, flags); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (crtc->state->event) { 2508c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 2518c2ecf20Sopenharmony_ci crtc->state->event = NULL; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ddev->event_lock, flags); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void tidss_crtc_atomic_disable(struct drm_crtc *crtc, 2588c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 2618c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 2628c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 2638c2ecf20Sopenharmony_ci unsigned long flags; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci reinit_completion(&tcrtc->framedone_completion); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&tcrtc->framedone_completion, 2728c2ecf20Sopenharmony_ci msecs_to_jiffies(500))) 2738c2ecf20Sopenharmony_ci dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d", 2748c2ecf20Sopenharmony_ci tcrtc->hw_videoport); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci spin_lock_irqsave(&ddev->event_lock, flags); 2798c2ecf20Sopenharmony_ci if (crtc->state->event) { 2808c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 2818c2ecf20Sopenharmony_ci crtc->state->event = NULL; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ddev->event_lock, flags); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci tidss_runtime_put(tidss); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic 2938c2ecf20Sopenharmony_cienum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc, 2948c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 2978c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 2988c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = { 3048c2ecf20Sopenharmony_ci .atomic_check = tidss_crtc_atomic_check, 3058c2ecf20Sopenharmony_ci .atomic_flush = tidss_crtc_atomic_flush, 3068c2ecf20Sopenharmony_ci .atomic_enable = tidss_crtc_atomic_enable, 3078c2ecf20Sopenharmony_ci .atomic_disable = tidss_crtc_atomic_disable, 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci .mode_valid = tidss_crtc_mode_valid, 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* drm_crtc_funcs */ 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int tidss_crtc_enable_vblank(struct drm_crtc *crtc) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 3178c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s\n", __func__); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci tidss_runtime_get(tidss); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci tidss_irq_enable_vblank(crtc); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void tidss_crtc_disable_vblank(struct drm_crtc *crtc) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct drm_device *ddev = crtc->dev; 3318c2ecf20Sopenharmony_ci struct tidss_device *tidss = to_tidss(ddev); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci dev_dbg(ddev->dev, "%s\n", __func__); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci tidss_irq_disable_vblank(crtc); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci tidss_runtime_put(tidss); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void tidss_crtc_reset(struct drm_crtc *crtc) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct tidss_crtc_state *tcrtc; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (crtc->state) 3458c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(crtc->state); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci kfree(crtc->state); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL); 3508c2ecf20Sopenharmony_ci if (!tcrtc) { 3518c2ecf20Sopenharmony_ci crtc->state = NULL; 3528c2ecf20Sopenharmony_ci return; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &tcrtc->base); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct tidss_crtc_state *state, *current_state; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (WARN_ON(!crtc->state)) 3638c2ecf20Sopenharmony_ci return NULL; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci current_state = to_tidss_crtc_state(crtc->state); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci state = kmalloc(sizeof(*state), GFP_KERNEL); 3688c2ecf20Sopenharmony_ci if (!state) 3698c2ecf20Sopenharmony_ci return NULL; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci state->plane_pos_changed = false; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci state->bus_format = current_state->bus_format; 3768c2ecf20Sopenharmony_ci state->bus_flags = current_state->bus_flags; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return &state->base; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic void tidss_crtc_destroy(struct drm_crtc *crtc) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc = to_tidss_crtc(crtc); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci drm_crtc_cleanup(crtc); 3868c2ecf20Sopenharmony_ci kfree(tcrtc); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs tidss_crtc_funcs = { 3908c2ecf20Sopenharmony_ci .reset = tidss_crtc_reset, 3918c2ecf20Sopenharmony_ci .destroy = tidss_crtc_destroy, 3928c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 3938c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 3948c2ecf20Sopenharmony_ci .atomic_duplicate_state = tidss_crtc_duplicate_state, 3958c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 3968c2ecf20Sopenharmony_ci .enable_vblank = tidss_crtc_enable_vblank, 3978c2ecf20Sopenharmony_ci .disable_vblank = tidss_crtc_disable_vblank, 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistruct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss, 4018c2ecf20Sopenharmony_ci u32 hw_videoport, 4028c2ecf20Sopenharmony_ci struct drm_plane *primary) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct tidss_crtc *tcrtc; 4058c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 4068c2ecf20Sopenharmony_ci unsigned int gamma_lut_size = 0; 4078c2ecf20Sopenharmony_ci bool has_ctm = tidss->feat->vp_feat.color.has_ctm; 4088c2ecf20Sopenharmony_ci int ret; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL); 4118c2ecf20Sopenharmony_ci if (!tcrtc) 4128c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci tcrtc->hw_videoport = hw_videoport; 4158c2ecf20Sopenharmony_ci init_completion(&tcrtc->framedone_completion); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci crtc = &tcrtc->crtc; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary, 4208c2ecf20Sopenharmony_ci NULL, &tidss_crtc_funcs, NULL); 4218c2ecf20Sopenharmony_ci if (ret < 0) { 4228c2ecf20Sopenharmony_ci kfree(tcrtc); 4238c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* 4298c2ecf20Sopenharmony_ci * The dispc gamma functions adapt to what ever size we ask 4308c2ecf20Sopenharmony_ci * from it no matter what HW supports. X-server assumes 256 4318c2ecf20Sopenharmony_ci * element gamma tables so lets use that. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci if (tidss->feat->vp_feat.color.gamma_size) 4348c2ecf20Sopenharmony_ci gamma_lut_size = 256; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size); 4378c2ecf20Sopenharmony_ci if (gamma_lut_size) 4388c2ecf20Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return tcrtc; 4418c2ecf20Sopenharmony_ci} 442