xref: /openbmc/linux/drivers/gpu/drm/tidss/tidss_crtc.c (revision 351f950d)
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>
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,
215*351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
21632a1795fSJyri Sarha {
217*351f950dSMaxime Ripard 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
218*351f950dSMaxime Ripard 									 crtc);
21932a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
22032a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
22102bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
22232a1795fSJyri Sarha 	const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
22332a1795fSJyri Sarha 	unsigned long flags;
22432a1795fSJyri Sarha 	int r;
22532a1795fSJyri Sarha 
22632a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
22732a1795fSJyri Sarha 
22832a1795fSJyri Sarha 	tidss_runtime_get(tidss);
22932a1795fSJyri Sarha 
23032a1795fSJyri Sarha 	r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport,
23132a1795fSJyri Sarha 				  mode->clock * 1000);
23232a1795fSJyri Sarha 	if (r != 0)
23332a1795fSJyri Sarha 		return;
23432a1795fSJyri Sarha 
23532a1795fSJyri Sarha 	r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport);
23632a1795fSJyri Sarha 	if (r != 0)
23732a1795fSJyri Sarha 		return;
23832a1795fSJyri Sarha 
23932a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true);
240b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_state, true);
24132a1795fSJyri Sarha 
24232a1795fSJyri Sarha 	/* Turn vertical blanking interrupt reporting on. */
24332a1795fSJyri Sarha 	drm_crtc_vblank_on(crtc);
24432a1795fSJyri Sarha 
24532a1795fSJyri Sarha 	dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24632a1795fSJyri Sarha 
24732a1795fSJyri Sarha 	dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24832a1795fSJyri Sarha 
24932a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
25032a1795fSJyri Sarha 
25132a1795fSJyri Sarha 	if (crtc->state->event) {
25232a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
25332a1795fSJyri Sarha 		crtc->state->event = NULL;
25432a1795fSJyri Sarha 	}
25532a1795fSJyri Sarha 
25632a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
25732a1795fSJyri Sarha }
25832a1795fSJyri Sarha 
25932a1795fSJyri Sarha static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,
260*351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
26132a1795fSJyri Sarha {
26232a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
26332a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
26402bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
26532a1795fSJyri Sarha 	unsigned long flags;
26632a1795fSJyri Sarha 
26732a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
26832a1795fSJyri Sarha 
26932a1795fSJyri Sarha 	reinit_completion(&tcrtc->framedone_completion);
27032a1795fSJyri Sarha 
27132a1795fSJyri Sarha 	dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport);
27232a1795fSJyri Sarha 
27332a1795fSJyri Sarha 	if (!wait_for_completion_timeout(&tcrtc->framedone_completion,
27432a1795fSJyri Sarha 					 msecs_to_jiffies(500)))
27532a1795fSJyri Sarha 		dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d",
27632a1795fSJyri Sarha 			tcrtc->hw_videoport);
27732a1795fSJyri Sarha 
27832a1795fSJyri Sarha 	dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport);
27932a1795fSJyri Sarha 
28032a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
28132a1795fSJyri Sarha 	if (crtc->state->event) {
28232a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
28332a1795fSJyri Sarha 		crtc->state->event = NULL;
28432a1795fSJyri Sarha 	}
28532a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
28632a1795fSJyri Sarha 
28732a1795fSJyri Sarha 	drm_crtc_vblank_off(crtc);
28832a1795fSJyri Sarha 
28932a1795fSJyri Sarha 	dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport);
29032a1795fSJyri Sarha 
29132a1795fSJyri Sarha 	tidss_runtime_put(tidss);
29232a1795fSJyri Sarha }
29332a1795fSJyri Sarha 
29432a1795fSJyri Sarha static
29532a1795fSJyri Sarha enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc,
29632a1795fSJyri Sarha 					   const struct drm_display_mode *mode)
29732a1795fSJyri Sarha {
29832a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
29932a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
30002bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
30132a1795fSJyri Sarha 
30232a1795fSJyri Sarha 	return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode);
30332a1795fSJyri Sarha }
30432a1795fSJyri Sarha 
30532a1795fSJyri Sarha static const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = {
30632a1795fSJyri Sarha 	.atomic_check = tidss_crtc_atomic_check,
30732a1795fSJyri Sarha 	.atomic_flush = tidss_crtc_atomic_flush,
30832a1795fSJyri Sarha 	.atomic_enable = tidss_crtc_atomic_enable,
30932a1795fSJyri Sarha 	.atomic_disable = tidss_crtc_atomic_disable,
31032a1795fSJyri Sarha 
31132a1795fSJyri Sarha 	.mode_valid = tidss_crtc_mode_valid,
31232a1795fSJyri Sarha };
31332a1795fSJyri Sarha 
31432a1795fSJyri Sarha /* drm_crtc_funcs */
31532a1795fSJyri Sarha 
31632a1795fSJyri Sarha static int tidss_crtc_enable_vblank(struct drm_crtc *crtc)
31732a1795fSJyri Sarha {
31832a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
31902bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
32032a1795fSJyri Sarha 
32132a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
32232a1795fSJyri Sarha 
32332a1795fSJyri Sarha 	tidss_runtime_get(tidss);
32432a1795fSJyri Sarha 
32532a1795fSJyri Sarha 	tidss_irq_enable_vblank(crtc);
32632a1795fSJyri Sarha 
32732a1795fSJyri Sarha 	return 0;
32832a1795fSJyri Sarha }
32932a1795fSJyri Sarha 
33032a1795fSJyri Sarha static void tidss_crtc_disable_vblank(struct drm_crtc *crtc)
33132a1795fSJyri Sarha {
33232a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
33302bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
33432a1795fSJyri Sarha 
33532a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
33632a1795fSJyri Sarha 
33732a1795fSJyri Sarha 	tidss_irq_disable_vblank(crtc);
33832a1795fSJyri Sarha 
33932a1795fSJyri Sarha 	tidss_runtime_put(tidss);
34032a1795fSJyri Sarha }
34132a1795fSJyri Sarha 
34232a1795fSJyri Sarha static void tidss_crtc_reset(struct drm_crtc *crtc)
34332a1795fSJyri Sarha {
34432a1795fSJyri Sarha 	struct tidss_crtc_state *tcrtc;
34532a1795fSJyri Sarha 
34632a1795fSJyri Sarha 	if (crtc->state)
34732a1795fSJyri Sarha 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
34832a1795fSJyri Sarha 
34932a1795fSJyri Sarha 	kfree(crtc->state);
35032a1795fSJyri Sarha 
35132a1795fSJyri Sarha 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
35232a1795fSJyri Sarha 	if (!tcrtc) {
35332a1795fSJyri Sarha 		crtc->state = NULL;
35432a1795fSJyri Sarha 		return;
35532a1795fSJyri Sarha 	}
35632a1795fSJyri Sarha 
35751f644b4SDaniel Vetter 	__drm_atomic_helper_crtc_reset(crtc, &tcrtc->base);
35832a1795fSJyri Sarha }
35932a1795fSJyri Sarha 
36032a1795fSJyri Sarha static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
36132a1795fSJyri Sarha {
36232a1795fSJyri Sarha 	struct tidss_crtc_state *state, *current_state;
36332a1795fSJyri Sarha 
36432a1795fSJyri Sarha 	if (WARN_ON(!crtc->state))
36532a1795fSJyri Sarha 		return NULL;
36632a1795fSJyri Sarha 
36732a1795fSJyri Sarha 	current_state = to_tidss_crtc_state(crtc->state);
36832a1795fSJyri Sarha 
36932a1795fSJyri Sarha 	state = kmalloc(sizeof(*state), GFP_KERNEL);
37032a1795fSJyri Sarha 	if (!state)
37132a1795fSJyri Sarha 		return NULL;
37232a1795fSJyri Sarha 
37332a1795fSJyri Sarha 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
37432a1795fSJyri Sarha 
375b33b5474SJyri Sarha 	state->plane_pos_changed = false;
376b33b5474SJyri Sarha 
37732a1795fSJyri Sarha 	state->bus_format = current_state->bus_format;
37832a1795fSJyri Sarha 	state->bus_flags = current_state->bus_flags;
37932a1795fSJyri Sarha 
38032a1795fSJyri Sarha 	return &state->base;
38132a1795fSJyri Sarha }
38232a1795fSJyri Sarha 
3839da67433STomi Valkeinen static void tidss_crtc_destroy(struct drm_crtc *crtc)
3849da67433STomi Valkeinen {
3859da67433STomi Valkeinen 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
3869da67433STomi Valkeinen 
3879da67433STomi Valkeinen 	drm_crtc_cleanup(crtc);
3889da67433STomi Valkeinen 	kfree(tcrtc);
3899da67433STomi Valkeinen }
3909da67433STomi Valkeinen 
39132a1795fSJyri Sarha static const struct drm_crtc_funcs tidss_crtc_funcs = {
39232a1795fSJyri Sarha 	.reset = tidss_crtc_reset,
3939da67433STomi Valkeinen 	.destroy = tidss_crtc_destroy,
39432a1795fSJyri Sarha 	.set_config = drm_atomic_helper_set_config,
39532a1795fSJyri Sarha 	.page_flip = drm_atomic_helper_page_flip,
39632a1795fSJyri Sarha 	.atomic_duplicate_state = tidss_crtc_duplicate_state,
39732a1795fSJyri Sarha 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
39832a1795fSJyri Sarha 	.enable_vblank = tidss_crtc_enable_vblank,
39932a1795fSJyri Sarha 	.disable_vblank = tidss_crtc_disable_vblank,
40032a1795fSJyri Sarha };
40132a1795fSJyri Sarha 
40232a1795fSJyri Sarha struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,
40332a1795fSJyri Sarha 				     u32 hw_videoport,
40432a1795fSJyri Sarha 				     struct drm_plane *primary)
40532a1795fSJyri Sarha {
40632a1795fSJyri Sarha 	struct tidss_crtc *tcrtc;
40732a1795fSJyri Sarha 	struct drm_crtc *crtc;
40832a1795fSJyri Sarha 	unsigned int gamma_lut_size = 0;
40932a1795fSJyri Sarha 	bool has_ctm = tidss->feat->vp_feat.color.has_ctm;
41032a1795fSJyri Sarha 	int ret;
41132a1795fSJyri Sarha 
4129da67433STomi Valkeinen 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
41332a1795fSJyri Sarha 	if (!tcrtc)
41432a1795fSJyri Sarha 		return ERR_PTR(-ENOMEM);
41532a1795fSJyri Sarha 
41632a1795fSJyri Sarha 	tcrtc->hw_videoport = hw_videoport;
41732a1795fSJyri Sarha 	init_completion(&tcrtc->framedone_completion);
41832a1795fSJyri Sarha 
41932a1795fSJyri Sarha 	crtc =  &tcrtc->crtc;
42032a1795fSJyri Sarha 
42132a1795fSJyri Sarha 	ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary,
42232a1795fSJyri Sarha 					NULL, &tidss_crtc_funcs, NULL);
4239da67433STomi Valkeinen 	if (ret < 0) {
4249da67433STomi Valkeinen 		kfree(tcrtc);
42532a1795fSJyri Sarha 		return ERR_PTR(ret);
4269da67433STomi Valkeinen 	}
42732a1795fSJyri Sarha 
42832a1795fSJyri Sarha 	drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs);
42932a1795fSJyri Sarha 
43032a1795fSJyri Sarha 	/*
43132a1795fSJyri Sarha 	 * The dispc gamma functions adapt to what ever size we ask
43232a1795fSJyri Sarha 	 * from it no matter what HW supports. X-server assumes 256
43332a1795fSJyri Sarha 	 * element gamma tables so lets use that.
43432a1795fSJyri Sarha 	 */
43532a1795fSJyri Sarha 	if (tidss->feat->vp_feat.color.gamma_size)
43632a1795fSJyri Sarha 		gamma_lut_size = 256;
43732a1795fSJyri Sarha 
43832a1795fSJyri Sarha 	drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size);
43932a1795fSJyri Sarha 	if (gamma_lut_size)
44032a1795fSJyri Sarha 		drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size);
44132a1795fSJyri Sarha 
44232a1795fSJyri Sarha 	return tcrtc;
44332a1795fSJyri Sarha }
444