xref: /openbmc/linux/drivers/gpu/drm/tidss/tidss_crtc.c (revision 4a83c26a)
132a1795fSJyri Sarha // SPDX-License-Identifier: GPL-2.0
232a1795fSJyri Sarha /*
39410113fSAlexander A. Klimov  * Copyright (C) 2018 Texas Instruments Incorporated - https://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>
11*4a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
1232a1795fSJyri Sarha #include <drm/drm_vblank.h>
1332a1795fSJyri Sarha 
1432a1795fSJyri Sarha #include "tidss_crtc.h"
1532a1795fSJyri Sarha #include "tidss_dispc.h"
1632a1795fSJyri Sarha #include "tidss_drv.h"
1732a1795fSJyri Sarha #include "tidss_irq.h"
18b33b5474SJyri Sarha #include "tidss_plane.h"
1932a1795fSJyri Sarha 
2032a1795fSJyri Sarha /* Page flip and frame done IRQs */
2132a1795fSJyri Sarha 
2232a1795fSJyri Sarha static void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc)
2332a1795fSJyri Sarha {
2432a1795fSJyri Sarha 	struct drm_device *ddev = tcrtc->crtc.dev;
2502bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
2632a1795fSJyri Sarha 	struct drm_pending_vblank_event *event;
2732a1795fSJyri Sarha 	unsigned long flags;
2832a1795fSJyri Sarha 	bool busy;
2932a1795fSJyri Sarha 
3032a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
3132a1795fSJyri Sarha 
3232a1795fSJyri Sarha 	/*
3332a1795fSJyri Sarha 	 * New settings are taken into use at VFP, and GO bit is cleared at
3432a1795fSJyri Sarha 	 * the same time. This happens before the vertical blank interrupt.
3532a1795fSJyri Sarha 	 * So there is a small change that the driver sets GO bit after VFP, but
3632a1795fSJyri Sarha 	 * before vblank, and we have to check for that case here.
3732a1795fSJyri Sarha 	 */
3832a1795fSJyri Sarha 	busy = dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport);
3932a1795fSJyri Sarha 	if (busy) {
4032a1795fSJyri Sarha 		spin_unlock_irqrestore(&ddev->event_lock, flags);
4132a1795fSJyri Sarha 		return;
4232a1795fSJyri Sarha 	}
4332a1795fSJyri Sarha 
4432a1795fSJyri Sarha 	event = tcrtc->event;
4532a1795fSJyri Sarha 	tcrtc->event = NULL;
4632a1795fSJyri Sarha 
4732a1795fSJyri Sarha 	if (!event) {
4832a1795fSJyri Sarha 		spin_unlock_irqrestore(&ddev->event_lock, flags);
4932a1795fSJyri Sarha 		return;
5032a1795fSJyri Sarha 	}
5132a1795fSJyri Sarha 
5232a1795fSJyri Sarha 	drm_crtc_send_vblank_event(&tcrtc->crtc, event);
5332a1795fSJyri Sarha 
5432a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
5532a1795fSJyri Sarha 
5632a1795fSJyri Sarha 	drm_crtc_vblank_put(&tcrtc->crtc);
5732a1795fSJyri Sarha }
5832a1795fSJyri Sarha 
5932a1795fSJyri Sarha void tidss_crtc_vblank_irq(struct drm_crtc *crtc)
6032a1795fSJyri Sarha {
6132a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
6232a1795fSJyri Sarha 
6332a1795fSJyri Sarha 	drm_crtc_handle_vblank(crtc);
6432a1795fSJyri Sarha 
6532a1795fSJyri Sarha 	tidss_crtc_finish_page_flip(tcrtc);
6632a1795fSJyri Sarha }
6732a1795fSJyri Sarha 
6832a1795fSJyri Sarha void tidss_crtc_framedone_irq(struct drm_crtc *crtc)
6932a1795fSJyri Sarha {
7032a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
7132a1795fSJyri Sarha 
7232a1795fSJyri Sarha 	complete(&tcrtc->framedone_completion);
7332a1795fSJyri Sarha }
7432a1795fSJyri Sarha 
7532a1795fSJyri Sarha void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus)
7632a1795fSJyri Sarha {
7732a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
7832a1795fSJyri Sarha 
7932a1795fSJyri Sarha 	dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n",
8032a1795fSJyri Sarha 			    tcrtc->hw_videoport, irqstatus);
8132a1795fSJyri Sarha }
8232a1795fSJyri Sarha 
8332a1795fSJyri Sarha /* drm_crtc_helper_funcs */
8432a1795fSJyri Sarha 
8532a1795fSJyri Sarha static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
8629b77ad7SMaxime Ripard 				   struct drm_atomic_state *state)
8732a1795fSJyri Sarha {
8829b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
8929b77ad7SMaxime Ripard 									  crtc);
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 
10029b77ad7SMaxime Ripard 	if (!crtc_state->enable)
10132a1795fSJyri Sarha 		return 0;
10232a1795fSJyri Sarha 
10329b77ad7SMaxime Ripard 	mode = &crtc_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 
11229b77ad7SMaxime Ripard 	return dispc_vp_bus_check(dispc, hw_videoport, crtc_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,
164f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
16532a1795fSJyri Sarha {
166f6ebe9f9SMaxime Ripard 	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
167f6ebe9f9SMaxime Ripard 									      crtc);
16832a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
16932a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
17002bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
17132a1795fSJyri Sarha 	unsigned long flags;
17232a1795fSJyri Sarha 
17332a1795fSJyri Sarha 	dev_dbg(ddev->dev,
17432a1795fSJyri Sarha 		"%s: %s enabled %d, needs modeset %d, event %p\n", __func__,
17532a1795fSJyri Sarha 		crtc->name, drm_atomic_crtc_needs_modeset(crtc->state),
17632a1795fSJyri Sarha 		crtc->state->enable, crtc->state->event);
17732a1795fSJyri Sarha 
17832a1795fSJyri Sarha 	/* There is nothing to do if CRTC is not going to be enabled. */
17932a1795fSJyri Sarha 	if (!crtc->state->enable)
18032a1795fSJyri Sarha 		return;
18132a1795fSJyri Sarha 
18232a1795fSJyri Sarha 	/*
18332a1795fSJyri Sarha 	 * Flush CRTC changes with go bit only if new modeset is not
18432a1795fSJyri Sarha 	 * coming, so CRTC is enabled trough out the commit.
18532a1795fSJyri Sarha 	 */
18632a1795fSJyri Sarha 	if (drm_atomic_crtc_needs_modeset(crtc->state))
18732a1795fSJyri Sarha 		return;
18832a1795fSJyri Sarha 
18932a1795fSJyri Sarha 	/* If the GO bit is stuck we better quit here. */
19032a1795fSJyri Sarha 	if (WARN_ON(dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport)))
19132a1795fSJyri Sarha 		return;
19232a1795fSJyri Sarha 
19332a1795fSJyri Sarha 	/* We should have event if CRTC is enabled through out this commit. */
19432a1795fSJyri Sarha 	if (WARN_ON(!crtc->state->event))
19532a1795fSJyri Sarha 		return;
19632a1795fSJyri Sarha 
19732a1795fSJyri Sarha 	/* Write vp properties to HW if needed. */
19832a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, false);
19932a1795fSJyri Sarha 
200b33b5474SJyri Sarha 	/* Update plane positions if needed. */
201b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_crtc_state, false);
202b33b5474SJyri Sarha 
20332a1795fSJyri Sarha 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
20432a1795fSJyri Sarha 
20532a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
20632a1795fSJyri Sarha 	dispc_vp_go(tidss->dispc, tcrtc->hw_videoport);
20732a1795fSJyri Sarha 
20832a1795fSJyri Sarha 	WARN_ON(tcrtc->event);
20932a1795fSJyri Sarha 
21032a1795fSJyri Sarha 	tcrtc->event = crtc->state->event;
21132a1795fSJyri Sarha 	crtc->state->event = NULL;
21232a1795fSJyri Sarha 
21332a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
21432a1795fSJyri Sarha }
21532a1795fSJyri Sarha 
21632a1795fSJyri Sarha static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,
217351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
21832a1795fSJyri Sarha {
219351f950dSMaxime Ripard 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
220351f950dSMaxime Ripard 									 crtc);
22132a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
22232a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
22302bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
22432a1795fSJyri Sarha 	const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
22532a1795fSJyri Sarha 	unsigned long flags;
22632a1795fSJyri Sarha 	int r;
22732a1795fSJyri Sarha 
22832a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
22932a1795fSJyri Sarha 
23032a1795fSJyri Sarha 	tidss_runtime_get(tidss);
23132a1795fSJyri Sarha 
23232a1795fSJyri Sarha 	r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport,
23332a1795fSJyri Sarha 				  mode->clock * 1000);
23432a1795fSJyri Sarha 	if (r != 0)
23532a1795fSJyri Sarha 		return;
23632a1795fSJyri Sarha 
23732a1795fSJyri Sarha 	r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport);
23832a1795fSJyri Sarha 	if (r != 0)
23932a1795fSJyri Sarha 		return;
24032a1795fSJyri Sarha 
24132a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true);
242b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_state, true);
24332a1795fSJyri Sarha 
24432a1795fSJyri Sarha 	/* Turn vertical blanking interrupt reporting on. */
24532a1795fSJyri Sarha 	drm_crtc_vblank_on(crtc);
24632a1795fSJyri Sarha 
24732a1795fSJyri Sarha 	dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24832a1795fSJyri Sarha 
24932a1795fSJyri Sarha 	dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state);
25032a1795fSJyri Sarha 
25132a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
25232a1795fSJyri Sarha 
25332a1795fSJyri Sarha 	if (crtc->state->event) {
25432a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
25532a1795fSJyri Sarha 		crtc->state->event = NULL;
25632a1795fSJyri Sarha 	}
25732a1795fSJyri Sarha 
25832a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
25932a1795fSJyri Sarha }
26032a1795fSJyri Sarha 
26132a1795fSJyri Sarha static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,
262351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
26332a1795fSJyri Sarha {
26432a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
26532a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
26602bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
26732a1795fSJyri Sarha 	unsigned long flags;
26832a1795fSJyri Sarha 
26932a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
27032a1795fSJyri Sarha 
27132a1795fSJyri Sarha 	reinit_completion(&tcrtc->framedone_completion);
27232a1795fSJyri Sarha 
27332a1795fSJyri Sarha 	dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport);
27432a1795fSJyri Sarha 
27532a1795fSJyri Sarha 	if (!wait_for_completion_timeout(&tcrtc->framedone_completion,
27632a1795fSJyri Sarha 					 msecs_to_jiffies(500)))
27732a1795fSJyri Sarha 		dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d",
27832a1795fSJyri Sarha 			tcrtc->hw_videoport);
27932a1795fSJyri Sarha 
28032a1795fSJyri Sarha 	dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport);
28132a1795fSJyri Sarha 
28232a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
28332a1795fSJyri Sarha 	if (crtc->state->event) {
28432a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
28532a1795fSJyri Sarha 		crtc->state->event = NULL;
28632a1795fSJyri Sarha 	}
28732a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
28832a1795fSJyri Sarha 
28932a1795fSJyri Sarha 	drm_crtc_vblank_off(crtc);
29032a1795fSJyri Sarha 
29132a1795fSJyri Sarha 	dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport);
29232a1795fSJyri Sarha 
29332a1795fSJyri Sarha 	tidss_runtime_put(tidss);
29432a1795fSJyri Sarha }
29532a1795fSJyri Sarha 
29632a1795fSJyri Sarha static
29732a1795fSJyri Sarha enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc,
29832a1795fSJyri Sarha 					   const struct drm_display_mode *mode)
29932a1795fSJyri Sarha {
30032a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
30132a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
30202bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
30332a1795fSJyri Sarha 
30432a1795fSJyri Sarha 	return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode);
30532a1795fSJyri Sarha }
30632a1795fSJyri Sarha 
30732a1795fSJyri Sarha static const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = {
30832a1795fSJyri Sarha 	.atomic_check = tidss_crtc_atomic_check,
30932a1795fSJyri Sarha 	.atomic_flush = tidss_crtc_atomic_flush,
31032a1795fSJyri Sarha 	.atomic_enable = tidss_crtc_atomic_enable,
31132a1795fSJyri Sarha 	.atomic_disable = tidss_crtc_atomic_disable,
31232a1795fSJyri Sarha 
31332a1795fSJyri Sarha 	.mode_valid = tidss_crtc_mode_valid,
31432a1795fSJyri Sarha };
31532a1795fSJyri Sarha 
31632a1795fSJyri Sarha /* drm_crtc_funcs */
31732a1795fSJyri Sarha 
31832a1795fSJyri Sarha static int tidss_crtc_enable_vblank(struct drm_crtc *crtc)
31932a1795fSJyri Sarha {
32032a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
32102bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
32232a1795fSJyri Sarha 
32332a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
32432a1795fSJyri Sarha 
32532a1795fSJyri Sarha 	tidss_runtime_get(tidss);
32632a1795fSJyri Sarha 
32732a1795fSJyri Sarha 	tidss_irq_enable_vblank(crtc);
32832a1795fSJyri Sarha 
32932a1795fSJyri Sarha 	return 0;
33032a1795fSJyri Sarha }
33132a1795fSJyri Sarha 
33232a1795fSJyri Sarha static void tidss_crtc_disable_vblank(struct drm_crtc *crtc)
33332a1795fSJyri Sarha {
33432a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
33502bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
33632a1795fSJyri Sarha 
33732a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
33832a1795fSJyri Sarha 
33932a1795fSJyri Sarha 	tidss_irq_disable_vblank(crtc);
34032a1795fSJyri Sarha 
34132a1795fSJyri Sarha 	tidss_runtime_put(tidss);
34232a1795fSJyri Sarha }
34332a1795fSJyri Sarha 
34432a1795fSJyri Sarha static void tidss_crtc_reset(struct drm_crtc *crtc)
34532a1795fSJyri Sarha {
34632a1795fSJyri Sarha 	struct tidss_crtc_state *tcrtc;
34732a1795fSJyri Sarha 
34832a1795fSJyri Sarha 	if (crtc->state)
34932a1795fSJyri Sarha 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
35032a1795fSJyri Sarha 
35132a1795fSJyri Sarha 	kfree(crtc->state);
35232a1795fSJyri Sarha 
35332a1795fSJyri Sarha 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
35432a1795fSJyri Sarha 	if (!tcrtc) {
35532a1795fSJyri Sarha 		crtc->state = NULL;
35632a1795fSJyri Sarha 		return;
35732a1795fSJyri Sarha 	}
35832a1795fSJyri Sarha 
35951f644b4SDaniel Vetter 	__drm_atomic_helper_crtc_reset(crtc, &tcrtc->base);
36032a1795fSJyri Sarha }
36132a1795fSJyri Sarha 
36232a1795fSJyri Sarha static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
36332a1795fSJyri Sarha {
36432a1795fSJyri Sarha 	struct tidss_crtc_state *state, *current_state;
36532a1795fSJyri Sarha 
36632a1795fSJyri Sarha 	if (WARN_ON(!crtc->state))
36732a1795fSJyri Sarha 		return NULL;
36832a1795fSJyri Sarha 
36932a1795fSJyri Sarha 	current_state = to_tidss_crtc_state(crtc->state);
37032a1795fSJyri Sarha 
37132a1795fSJyri Sarha 	state = kmalloc(sizeof(*state), GFP_KERNEL);
37232a1795fSJyri Sarha 	if (!state)
37332a1795fSJyri Sarha 		return NULL;
37432a1795fSJyri Sarha 
37532a1795fSJyri Sarha 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
37632a1795fSJyri Sarha 
377b33b5474SJyri Sarha 	state->plane_pos_changed = false;
378b33b5474SJyri Sarha 
37932a1795fSJyri Sarha 	state->bus_format = current_state->bus_format;
38032a1795fSJyri Sarha 	state->bus_flags = current_state->bus_flags;
38132a1795fSJyri Sarha 
38232a1795fSJyri Sarha 	return &state->base;
38332a1795fSJyri Sarha }
38432a1795fSJyri Sarha 
3859da67433STomi Valkeinen static void tidss_crtc_destroy(struct drm_crtc *crtc)
3869da67433STomi Valkeinen {
3879da67433STomi Valkeinen 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
3889da67433STomi Valkeinen 
3899da67433STomi Valkeinen 	drm_crtc_cleanup(crtc);
3909da67433STomi Valkeinen 	kfree(tcrtc);
3919da67433STomi Valkeinen }
3929da67433STomi Valkeinen 
39332a1795fSJyri Sarha static const struct drm_crtc_funcs tidss_crtc_funcs = {
39432a1795fSJyri Sarha 	.reset = tidss_crtc_reset,
3959da67433STomi Valkeinen 	.destroy = tidss_crtc_destroy,
39632a1795fSJyri Sarha 	.set_config = drm_atomic_helper_set_config,
39732a1795fSJyri Sarha 	.page_flip = drm_atomic_helper_page_flip,
39832a1795fSJyri Sarha 	.atomic_duplicate_state = tidss_crtc_duplicate_state,
39932a1795fSJyri Sarha 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
40032a1795fSJyri Sarha 	.enable_vblank = tidss_crtc_enable_vblank,
40132a1795fSJyri Sarha 	.disable_vblank = tidss_crtc_disable_vblank,
40232a1795fSJyri Sarha };
40332a1795fSJyri Sarha 
40432a1795fSJyri Sarha struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,
40532a1795fSJyri Sarha 				     u32 hw_videoport,
40632a1795fSJyri Sarha 				     struct drm_plane *primary)
40732a1795fSJyri Sarha {
40832a1795fSJyri Sarha 	struct tidss_crtc *tcrtc;
40932a1795fSJyri Sarha 	struct drm_crtc *crtc;
41032a1795fSJyri Sarha 	unsigned int gamma_lut_size = 0;
41132a1795fSJyri Sarha 	bool has_ctm = tidss->feat->vp_feat.color.has_ctm;
41232a1795fSJyri Sarha 	int ret;
41332a1795fSJyri Sarha 
4149da67433STomi Valkeinen 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
41532a1795fSJyri Sarha 	if (!tcrtc)
41632a1795fSJyri Sarha 		return ERR_PTR(-ENOMEM);
41732a1795fSJyri Sarha 
41832a1795fSJyri Sarha 	tcrtc->hw_videoport = hw_videoport;
41932a1795fSJyri Sarha 	init_completion(&tcrtc->framedone_completion);
42032a1795fSJyri Sarha 
42132a1795fSJyri Sarha 	crtc =  &tcrtc->crtc;
42232a1795fSJyri Sarha 
42332a1795fSJyri Sarha 	ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary,
42432a1795fSJyri Sarha 					NULL, &tidss_crtc_funcs, NULL);
4259da67433STomi Valkeinen 	if (ret < 0) {
4269da67433STomi Valkeinen 		kfree(tcrtc);
42732a1795fSJyri Sarha 		return ERR_PTR(ret);
4289da67433STomi Valkeinen 	}
42932a1795fSJyri Sarha 
43032a1795fSJyri Sarha 	drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs);
43132a1795fSJyri Sarha 
43232a1795fSJyri Sarha 	/*
43332a1795fSJyri Sarha 	 * The dispc gamma functions adapt to what ever size we ask
43432a1795fSJyri Sarha 	 * from it no matter what HW supports. X-server assumes 256
43532a1795fSJyri Sarha 	 * element gamma tables so lets use that.
43632a1795fSJyri Sarha 	 */
43732a1795fSJyri Sarha 	if (tidss->feat->vp_feat.color.gamma_size)
43832a1795fSJyri Sarha 		gamma_lut_size = 256;
43932a1795fSJyri Sarha 
44032a1795fSJyri Sarha 	drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size);
44132a1795fSJyri Sarha 	if (gamma_lut_size)
44232a1795fSJyri Sarha 		drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size);
44332a1795fSJyri Sarha 
44432a1795fSJyri Sarha 	return tcrtc;
44532a1795fSJyri Sarha }
446