xref: /openbmc/linux/drivers/gpu/drm/tidss/tidss_crtc.c (revision 02bb1317)
132a1795fSJyri Sarha // SPDX-License-Identifier: GPL-2.0
232a1795fSJyri Sarha /*
332a1795fSJyri Sarha  * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
432a1795fSJyri Sarha  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
532a1795fSJyri Sarha  */
632a1795fSJyri Sarha 
732a1795fSJyri Sarha #include <drm/drm_atomic.h>
832a1795fSJyri Sarha #include <drm/drm_atomic_helper.h>
932a1795fSJyri Sarha #include <drm/drm_crtc.h>
1032a1795fSJyri Sarha #include <drm/drm_crtc_helper.h>
1132a1795fSJyri Sarha #include <drm/drm_fb_cma_helper.h>
1232a1795fSJyri Sarha #include <drm/drm_gem_cma_helper.h>
1332a1795fSJyri Sarha #include <drm/drm_plane_helper.h>
1432a1795fSJyri Sarha #include <drm/drm_vblank.h>
1532a1795fSJyri Sarha 
1632a1795fSJyri Sarha #include "tidss_crtc.h"
1732a1795fSJyri Sarha #include "tidss_dispc.h"
1832a1795fSJyri Sarha #include "tidss_drv.h"
1932a1795fSJyri Sarha #include "tidss_irq.h"
20b33b5474SJyri Sarha #include "tidss_plane.h"
2132a1795fSJyri Sarha 
2232a1795fSJyri Sarha /* Page flip and frame done IRQs */
2332a1795fSJyri Sarha 
2432a1795fSJyri Sarha static void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc)
2532a1795fSJyri Sarha {
2632a1795fSJyri Sarha 	struct drm_device *ddev = tcrtc->crtc.dev;
2702bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
2832a1795fSJyri Sarha 	struct drm_pending_vblank_event *event;
2932a1795fSJyri Sarha 	unsigned long flags;
3032a1795fSJyri Sarha 	bool busy;
3132a1795fSJyri Sarha 
3232a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
3332a1795fSJyri Sarha 
3432a1795fSJyri Sarha 	/*
3532a1795fSJyri Sarha 	 * New settings are taken into use at VFP, and GO bit is cleared at
3632a1795fSJyri Sarha 	 * the same time. This happens before the vertical blank interrupt.
3732a1795fSJyri Sarha 	 * So there is a small change that the driver sets GO bit after VFP, but
3832a1795fSJyri Sarha 	 * before vblank, and we have to check for that case here.
3932a1795fSJyri Sarha 	 */
4032a1795fSJyri Sarha 	busy = dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport);
4132a1795fSJyri Sarha 	if (busy) {
4232a1795fSJyri Sarha 		spin_unlock_irqrestore(&ddev->event_lock, flags);
4332a1795fSJyri Sarha 		return;
4432a1795fSJyri Sarha 	}
4532a1795fSJyri Sarha 
4632a1795fSJyri Sarha 	event = tcrtc->event;
4732a1795fSJyri Sarha 	tcrtc->event = NULL;
4832a1795fSJyri Sarha 
4932a1795fSJyri Sarha 	if (!event) {
5032a1795fSJyri Sarha 		spin_unlock_irqrestore(&ddev->event_lock, flags);
5132a1795fSJyri Sarha 		return;
5232a1795fSJyri Sarha 	}
5332a1795fSJyri Sarha 
5432a1795fSJyri Sarha 	drm_crtc_send_vblank_event(&tcrtc->crtc, event);
5532a1795fSJyri Sarha 
5632a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
5732a1795fSJyri Sarha 
5832a1795fSJyri Sarha 	drm_crtc_vblank_put(&tcrtc->crtc);
5932a1795fSJyri Sarha }
6032a1795fSJyri Sarha 
6132a1795fSJyri Sarha void tidss_crtc_vblank_irq(struct drm_crtc *crtc)
6232a1795fSJyri Sarha {
6332a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
6432a1795fSJyri Sarha 
6532a1795fSJyri Sarha 	drm_crtc_handle_vblank(crtc);
6632a1795fSJyri Sarha 
6732a1795fSJyri Sarha 	tidss_crtc_finish_page_flip(tcrtc);
6832a1795fSJyri Sarha }
6932a1795fSJyri Sarha 
7032a1795fSJyri Sarha void tidss_crtc_framedone_irq(struct drm_crtc *crtc)
7132a1795fSJyri Sarha {
7232a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
7332a1795fSJyri Sarha 
7432a1795fSJyri Sarha 	complete(&tcrtc->framedone_completion);
7532a1795fSJyri Sarha }
7632a1795fSJyri Sarha 
7732a1795fSJyri Sarha void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus)
7832a1795fSJyri Sarha {
7932a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
8032a1795fSJyri Sarha 
8132a1795fSJyri Sarha 	dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n",
8232a1795fSJyri Sarha 			    tcrtc->hw_videoport, irqstatus);
8332a1795fSJyri Sarha }
8432a1795fSJyri Sarha 
8532a1795fSJyri Sarha /* drm_crtc_helper_funcs */
8632a1795fSJyri Sarha 
8732a1795fSJyri Sarha static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
8832a1795fSJyri Sarha 				   struct drm_crtc_state *state)
8932a1795fSJyri Sarha {
9032a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
9102bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
9232a1795fSJyri Sarha 	struct dispc_device *dispc = tidss->dispc;
9332a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
9432a1795fSJyri Sarha 	u32 hw_videoport = tcrtc->hw_videoport;
9532a1795fSJyri Sarha 	const struct drm_display_mode *mode;
9632a1795fSJyri Sarha 	enum drm_mode_status ok;
9732a1795fSJyri Sarha 
9832a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
9932a1795fSJyri Sarha 
10032a1795fSJyri Sarha 	if (!state->enable)
10132a1795fSJyri Sarha 		return 0;
10232a1795fSJyri Sarha 
10332a1795fSJyri Sarha 	mode = &state->adjusted_mode;
10432a1795fSJyri Sarha 
10532a1795fSJyri Sarha 	ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
10632a1795fSJyri Sarha 	if (ok != MODE_OK) {
10732a1795fSJyri Sarha 		dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",
10832a1795fSJyri Sarha 			__func__, mode->hdisplay, mode->vdisplay, mode->clock);
10932a1795fSJyri Sarha 		return -EINVAL;
11032a1795fSJyri Sarha 	}
11132a1795fSJyri Sarha 
11232a1795fSJyri Sarha 	return dispc_vp_bus_check(dispc, hw_videoport, state);
11332a1795fSJyri Sarha }
11432a1795fSJyri Sarha 
115b33b5474SJyri Sarha /*
116b33b5474SJyri Sarha  * This needs all affected planes to be present in the atomic
117b33b5474SJyri Sarha  * state. The untouched planes are added to the state in
118b33b5474SJyri Sarha  * tidss_atomic_check().
119b33b5474SJyri Sarha  */
120b33b5474SJyri Sarha static void tidss_crtc_position_planes(struct tidss_device *tidss,
121b33b5474SJyri Sarha 				       struct drm_crtc *crtc,
122b33b5474SJyri Sarha 				       struct drm_crtc_state *old_state,
123b33b5474SJyri Sarha 				       bool newmodeset)
124b33b5474SJyri Sarha {
125b33b5474SJyri Sarha 	struct drm_atomic_state *ostate = old_state->state;
126b33b5474SJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
127b33b5474SJyri Sarha 	struct drm_crtc_state *cstate = crtc->state;
128b33b5474SJyri Sarha 	int layer;
129b33b5474SJyri Sarha 
130b33b5474SJyri Sarha 	if (!newmodeset && !cstate->zpos_changed &&
131b33b5474SJyri Sarha 	    !to_tidss_crtc_state(cstate)->plane_pos_changed)
132b33b5474SJyri Sarha 		return;
133b33b5474SJyri Sarha 
134b33b5474SJyri Sarha 	for (layer = 0; layer < tidss->feat->num_planes; layer++) {
135b33b5474SJyri Sarha 		struct drm_plane_state *pstate;
136b33b5474SJyri Sarha 		struct drm_plane *plane;
137b33b5474SJyri Sarha 		bool layer_active = false;
138b33b5474SJyri Sarha 		int i;
139b33b5474SJyri Sarha 
140b33b5474SJyri Sarha 		for_each_new_plane_in_state(ostate, plane, pstate, i) {
141b33b5474SJyri Sarha 			if (pstate->crtc != crtc || !pstate->visible)
142b33b5474SJyri Sarha 				continue;
143b33b5474SJyri Sarha 
144b33b5474SJyri Sarha 			if (pstate->normalized_zpos == layer) {
145b33b5474SJyri Sarha 				layer_active = true;
146b33b5474SJyri Sarha 				break;
147b33b5474SJyri Sarha 			}
148b33b5474SJyri Sarha 		}
149b33b5474SJyri Sarha 
150b33b5474SJyri Sarha 		if (layer_active) {
151b33b5474SJyri Sarha 			struct tidss_plane *tplane = to_tidss_plane(plane);
152b33b5474SJyri Sarha 
153b33b5474SJyri Sarha 			dispc_ovr_set_plane(tidss->dispc, tplane->hw_plane_id,
154b33b5474SJyri Sarha 					    tcrtc->hw_videoport,
155b33b5474SJyri Sarha 					    pstate->crtc_x, pstate->crtc_y,
156b33b5474SJyri Sarha 					    layer);
157b33b5474SJyri Sarha 		}
158b33b5474SJyri Sarha 		dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer,
159b33b5474SJyri Sarha 				       layer_active);
160b33b5474SJyri Sarha 	}
161b33b5474SJyri Sarha }
162b33b5474SJyri Sarha 
16332a1795fSJyri Sarha static void tidss_crtc_atomic_flush(struct drm_crtc *crtc,
16432a1795fSJyri Sarha 				    struct drm_crtc_state *old_crtc_state)
16532a1795fSJyri Sarha {
16632a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
16732a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
16802bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
16932a1795fSJyri Sarha 	unsigned long flags;
17032a1795fSJyri Sarha 
17132a1795fSJyri Sarha 	dev_dbg(ddev->dev,
17232a1795fSJyri Sarha 		"%s: %s enabled %d, needs modeset %d, event %p\n", __func__,
17332a1795fSJyri Sarha 		crtc->name, drm_atomic_crtc_needs_modeset(crtc->state),
17432a1795fSJyri Sarha 		crtc->state->enable, crtc->state->event);
17532a1795fSJyri Sarha 
17632a1795fSJyri Sarha 	/* There is nothing to do if CRTC is not going to be enabled. */
17732a1795fSJyri Sarha 	if (!crtc->state->enable)
17832a1795fSJyri Sarha 		return;
17932a1795fSJyri Sarha 
18032a1795fSJyri Sarha 	/*
18132a1795fSJyri Sarha 	 * Flush CRTC changes with go bit only if new modeset is not
18232a1795fSJyri Sarha 	 * coming, so CRTC is enabled trough out the commit.
18332a1795fSJyri Sarha 	 */
18432a1795fSJyri Sarha 	if (drm_atomic_crtc_needs_modeset(crtc->state))
18532a1795fSJyri Sarha 		return;
18632a1795fSJyri Sarha 
18732a1795fSJyri Sarha 	/* If the GO bit is stuck we better quit here. */
18832a1795fSJyri Sarha 	if (WARN_ON(dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport)))
18932a1795fSJyri Sarha 		return;
19032a1795fSJyri Sarha 
19132a1795fSJyri Sarha 	/* We should have event if CRTC is enabled through out this commit. */
19232a1795fSJyri Sarha 	if (WARN_ON(!crtc->state->event))
19332a1795fSJyri Sarha 		return;
19432a1795fSJyri Sarha 
19532a1795fSJyri Sarha 	/* Write vp properties to HW if needed. */
19632a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, false);
19732a1795fSJyri Sarha 
198b33b5474SJyri Sarha 	/* Update plane positions if needed. */
199b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_crtc_state, false);
200b33b5474SJyri Sarha 
20132a1795fSJyri Sarha 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
20232a1795fSJyri Sarha 
20332a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
20432a1795fSJyri Sarha 	dispc_vp_go(tidss->dispc, tcrtc->hw_videoport);
20532a1795fSJyri Sarha 
20632a1795fSJyri Sarha 	WARN_ON(tcrtc->event);
20732a1795fSJyri Sarha 
20832a1795fSJyri Sarha 	tcrtc->event = crtc->state->event;
20932a1795fSJyri Sarha 	crtc->state->event = NULL;
21032a1795fSJyri Sarha 
21132a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
21232a1795fSJyri Sarha }
21332a1795fSJyri Sarha 
21432a1795fSJyri Sarha static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,
21532a1795fSJyri Sarha 				     struct drm_crtc_state *old_state)
21632a1795fSJyri Sarha {
21732a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
21832a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
21902bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
22032a1795fSJyri Sarha 	const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
22132a1795fSJyri Sarha 	unsigned long flags;
22232a1795fSJyri Sarha 	int r;
22332a1795fSJyri Sarha 
22432a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
22532a1795fSJyri Sarha 
22632a1795fSJyri Sarha 	tidss_runtime_get(tidss);
22732a1795fSJyri Sarha 
22832a1795fSJyri Sarha 	r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport,
22932a1795fSJyri Sarha 				  mode->clock * 1000);
23032a1795fSJyri Sarha 	if (r != 0)
23132a1795fSJyri Sarha 		return;
23232a1795fSJyri Sarha 
23332a1795fSJyri Sarha 	r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport);
23432a1795fSJyri Sarha 	if (r != 0)
23532a1795fSJyri Sarha 		return;
23632a1795fSJyri Sarha 
23732a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true);
238b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_state, true);
23932a1795fSJyri Sarha 
24032a1795fSJyri Sarha 	/* Turn vertical blanking interrupt reporting on. */
24132a1795fSJyri Sarha 	drm_crtc_vblank_on(crtc);
24232a1795fSJyri Sarha 
24332a1795fSJyri Sarha 	dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24432a1795fSJyri Sarha 
24532a1795fSJyri Sarha 	dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24632a1795fSJyri Sarha 
24732a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
24832a1795fSJyri Sarha 
24932a1795fSJyri Sarha 	if (crtc->state->event) {
25032a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
25132a1795fSJyri Sarha 		crtc->state->event = NULL;
25232a1795fSJyri Sarha 	}
25332a1795fSJyri Sarha 
25432a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
25532a1795fSJyri Sarha }
25632a1795fSJyri Sarha 
25732a1795fSJyri Sarha static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,
25832a1795fSJyri Sarha 				      struct drm_crtc_state *old_state)
25932a1795fSJyri Sarha {
26032a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
26132a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
26202bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
26332a1795fSJyri Sarha 	unsigned long flags;
26432a1795fSJyri Sarha 
26532a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
26632a1795fSJyri Sarha 
26732a1795fSJyri Sarha 	reinit_completion(&tcrtc->framedone_completion);
26832a1795fSJyri Sarha 
26932a1795fSJyri Sarha 	dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport);
27032a1795fSJyri Sarha 
27132a1795fSJyri Sarha 	if (!wait_for_completion_timeout(&tcrtc->framedone_completion,
27232a1795fSJyri Sarha 					 msecs_to_jiffies(500)))
27332a1795fSJyri Sarha 		dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d",
27432a1795fSJyri Sarha 			tcrtc->hw_videoport);
27532a1795fSJyri Sarha 
27632a1795fSJyri Sarha 	dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport);
27732a1795fSJyri Sarha 
27832a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
27932a1795fSJyri Sarha 	if (crtc->state->event) {
28032a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
28132a1795fSJyri Sarha 		crtc->state->event = NULL;
28232a1795fSJyri Sarha 	}
28332a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
28432a1795fSJyri Sarha 
28532a1795fSJyri Sarha 	drm_crtc_vblank_off(crtc);
28632a1795fSJyri Sarha 
28732a1795fSJyri Sarha 	dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport);
28832a1795fSJyri Sarha 
28932a1795fSJyri Sarha 	tidss_runtime_put(tidss);
29032a1795fSJyri Sarha }
29132a1795fSJyri Sarha 
29232a1795fSJyri Sarha static
29332a1795fSJyri Sarha enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc,
29432a1795fSJyri Sarha 					   const struct drm_display_mode *mode)
29532a1795fSJyri Sarha {
29632a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
29732a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
29802bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
29932a1795fSJyri Sarha 
30032a1795fSJyri Sarha 	return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode);
30132a1795fSJyri Sarha }
30232a1795fSJyri Sarha 
30332a1795fSJyri Sarha static const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = {
30432a1795fSJyri Sarha 	.atomic_check = tidss_crtc_atomic_check,
30532a1795fSJyri Sarha 	.atomic_flush = tidss_crtc_atomic_flush,
30632a1795fSJyri Sarha 	.atomic_enable = tidss_crtc_atomic_enable,
30732a1795fSJyri Sarha 	.atomic_disable = tidss_crtc_atomic_disable,
30832a1795fSJyri Sarha 
30932a1795fSJyri Sarha 	.mode_valid = tidss_crtc_mode_valid,
31032a1795fSJyri Sarha };
31132a1795fSJyri Sarha 
31232a1795fSJyri Sarha /* drm_crtc_funcs */
31332a1795fSJyri Sarha 
31432a1795fSJyri Sarha static int tidss_crtc_enable_vblank(struct drm_crtc *crtc)
31532a1795fSJyri Sarha {
31632a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
31702bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
31832a1795fSJyri Sarha 
31932a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
32032a1795fSJyri Sarha 
32132a1795fSJyri Sarha 	tidss_runtime_get(tidss);
32232a1795fSJyri Sarha 
32332a1795fSJyri Sarha 	tidss_irq_enable_vblank(crtc);
32432a1795fSJyri Sarha 
32532a1795fSJyri Sarha 	return 0;
32632a1795fSJyri Sarha }
32732a1795fSJyri Sarha 
32832a1795fSJyri Sarha static void tidss_crtc_disable_vblank(struct drm_crtc *crtc)
32932a1795fSJyri Sarha {
33032a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
33102bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
33232a1795fSJyri Sarha 
33332a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
33432a1795fSJyri Sarha 
33532a1795fSJyri Sarha 	tidss_irq_disable_vblank(crtc);
33632a1795fSJyri Sarha 
33732a1795fSJyri Sarha 	tidss_runtime_put(tidss);
33832a1795fSJyri Sarha }
33932a1795fSJyri Sarha 
34032a1795fSJyri Sarha static void tidss_crtc_reset(struct drm_crtc *crtc)
34132a1795fSJyri Sarha {
34232a1795fSJyri Sarha 	struct tidss_crtc_state *tcrtc;
34332a1795fSJyri Sarha 
34432a1795fSJyri Sarha 	if (crtc->state)
34532a1795fSJyri Sarha 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
34632a1795fSJyri Sarha 
34732a1795fSJyri Sarha 	kfree(crtc->state);
34832a1795fSJyri Sarha 
34932a1795fSJyri Sarha 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
35032a1795fSJyri Sarha 	if (!tcrtc) {
35132a1795fSJyri Sarha 		crtc->state = NULL;
35232a1795fSJyri Sarha 		return;
35332a1795fSJyri Sarha 	}
35432a1795fSJyri Sarha 
35532a1795fSJyri Sarha 	crtc->state = &tcrtc->base;
35632a1795fSJyri Sarha 	crtc->state->crtc = crtc;
35732a1795fSJyri Sarha }
35832a1795fSJyri Sarha 
35932a1795fSJyri Sarha static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
36032a1795fSJyri Sarha {
36132a1795fSJyri Sarha 	struct tidss_crtc_state *state, *current_state;
36232a1795fSJyri Sarha 
36332a1795fSJyri Sarha 	if (WARN_ON(!crtc->state))
36432a1795fSJyri Sarha 		return NULL;
36532a1795fSJyri Sarha 
36632a1795fSJyri Sarha 	current_state = to_tidss_crtc_state(crtc->state);
36732a1795fSJyri Sarha 
36832a1795fSJyri Sarha 	state = kmalloc(sizeof(*state), GFP_KERNEL);
36932a1795fSJyri Sarha 	if (!state)
37032a1795fSJyri Sarha 		return NULL;
37132a1795fSJyri Sarha 
37232a1795fSJyri Sarha 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
37332a1795fSJyri Sarha 
374b33b5474SJyri Sarha 	state->plane_pos_changed = false;
375b33b5474SJyri Sarha 
37632a1795fSJyri Sarha 	state->bus_format = current_state->bus_format;
37732a1795fSJyri Sarha 	state->bus_flags = current_state->bus_flags;
37832a1795fSJyri Sarha 
37932a1795fSJyri Sarha 	return &state->base;
38032a1795fSJyri Sarha }
38132a1795fSJyri Sarha 
38232a1795fSJyri Sarha static const struct drm_crtc_funcs tidss_crtc_funcs = {
38332a1795fSJyri Sarha 	.reset = tidss_crtc_reset,
38432a1795fSJyri Sarha 	.destroy = drm_crtc_cleanup,
38532a1795fSJyri Sarha 	.set_config = drm_atomic_helper_set_config,
38632a1795fSJyri Sarha 	.page_flip = drm_atomic_helper_page_flip,
38732a1795fSJyri Sarha 	.atomic_duplicate_state = tidss_crtc_duplicate_state,
38832a1795fSJyri Sarha 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
38932a1795fSJyri Sarha 	.enable_vblank = tidss_crtc_enable_vblank,
39032a1795fSJyri Sarha 	.disable_vblank = tidss_crtc_disable_vblank,
39132a1795fSJyri Sarha };
39232a1795fSJyri Sarha 
39332a1795fSJyri Sarha struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,
39432a1795fSJyri Sarha 				     u32 hw_videoport,
39532a1795fSJyri Sarha 				     struct drm_plane *primary)
39632a1795fSJyri Sarha {
39732a1795fSJyri Sarha 	struct tidss_crtc *tcrtc;
39832a1795fSJyri Sarha 	struct drm_crtc *crtc;
39932a1795fSJyri Sarha 	unsigned int gamma_lut_size = 0;
40032a1795fSJyri Sarha 	bool has_ctm = tidss->feat->vp_feat.color.has_ctm;
40132a1795fSJyri Sarha 	int ret;
40232a1795fSJyri Sarha 
40332a1795fSJyri Sarha 	tcrtc = devm_kzalloc(tidss->dev, sizeof(*tcrtc), GFP_KERNEL);
40432a1795fSJyri Sarha 	if (!tcrtc)
40532a1795fSJyri Sarha 		return ERR_PTR(-ENOMEM);
40632a1795fSJyri Sarha 
40732a1795fSJyri Sarha 	tcrtc->hw_videoport = hw_videoport;
40832a1795fSJyri Sarha 	init_completion(&tcrtc->framedone_completion);
40932a1795fSJyri Sarha 
41032a1795fSJyri Sarha 	crtc =  &tcrtc->crtc;
41132a1795fSJyri Sarha 
41232a1795fSJyri Sarha 	ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary,
41332a1795fSJyri Sarha 					NULL, &tidss_crtc_funcs, NULL);
41432a1795fSJyri Sarha 	if (ret < 0)
41532a1795fSJyri Sarha 		return ERR_PTR(ret);
41632a1795fSJyri Sarha 
41732a1795fSJyri Sarha 	drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs);
41832a1795fSJyri Sarha 
41932a1795fSJyri Sarha 	/*
42032a1795fSJyri Sarha 	 * The dispc gamma functions adapt to what ever size we ask
42132a1795fSJyri Sarha 	 * from it no matter what HW supports. X-server assumes 256
42232a1795fSJyri Sarha 	 * element gamma tables so lets use that.
42332a1795fSJyri Sarha 	 */
42432a1795fSJyri Sarha 	if (tidss->feat->vp_feat.color.gamma_size)
42532a1795fSJyri Sarha 		gamma_lut_size = 256;
42632a1795fSJyri Sarha 
42732a1795fSJyri Sarha 	drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size);
42832a1795fSJyri Sarha 	if (gamma_lut_size)
42932a1795fSJyri Sarha 		drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size);
43032a1795fSJyri Sarha 
43132a1795fSJyri Sarha 	return tcrtc;
43232a1795fSJyri Sarha }
433