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