xref: /openbmc/linux/drivers/gpu/drm/tidss/tidss_crtc.c (revision 1dce1cee)
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>
104a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
1132a1795fSJyri Sarha #include <drm/drm_vblank.h>
1232a1795fSJyri Sarha 
1332a1795fSJyri Sarha #include "tidss_crtc.h"
1432a1795fSJyri Sarha #include "tidss_dispc.h"
1532a1795fSJyri Sarha #include "tidss_drv.h"
1632a1795fSJyri Sarha #include "tidss_irq.h"
17b33b5474SJyri Sarha #include "tidss_plane.h"
1832a1795fSJyri Sarha 
1932a1795fSJyri Sarha /* Page flip and frame done IRQs */
2032a1795fSJyri Sarha 
tidss_crtc_finish_page_flip(struct tidss_crtc * tcrtc)2132a1795fSJyri Sarha static void tidss_crtc_finish_page_flip(struct tidss_crtc *tcrtc)
2232a1795fSJyri Sarha {
2332a1795fSJyri Sarha 	struct drm_device *ddev = tcrtc->crtc.dev;
2402bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
2532a1795fSJyri Sarha 	struct drm_pending_vblank_event *event;
2632a1795fSJyri Sarha 	unsigned long flags;
2732a1795fSJyri Sarha 	bool busy;
2832a1795fSJyri Sarha 
2932a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
3032a1795fSJyri Sarha 
3132a1795fSJyri Sarha 	/*
3232a1795fSJyri Sarha 	 * New settings are taken into use at VFP, and GO bit is cleared at
3332a1795fSJyri Sarha 	 * the same time. This happens before the vertical blank interrupt.
3432a1795fSJyri Sarha 	 * So there is a small change that the driver sets GO bit after VFP, but
3532a1795fSJyri Sarha 	 * before vblank, and we have to check for that case here.
3632a1795fSJyri Sarha 	 */
3732a1795fSJyri Sarha 	busy = dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport);
3832a1795fSJyri Sarha 	if (busy) {
3932a1795fSJyri Sarha 		spin_unlock_irqrestore(&ddev->event_lock, flags);
4032a1795fSJyri Sarha 		return;
4132a1795fSJyri Sarha 	}
4232a1795fSJyri Sarha 
4332a1795fSJyri Sarha 	event = tcrtc->event;
4432a1795fSJyri Sarha 	tcrtc->event = NULL;
4532a1795fSJyri Sarha 
4632a1795fSJyri Sarha 	if (!event) {
4732a1795fSJyri Sarha 		spin_unlock_irqrestore(&ddev->event_lock, flags);
4832a1795fSJyri Sarha 		return;
4932a1795fSJyri Sarha 	}
5032a1795fSJyri Sarha 
5132a1795fSJyri Sarha 	drm_crtc_send_vblank_event(&tcrtc->crtc, event);
5232a1795fSJyri Sarha 
5332a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
5432a1795fSJyri Sarha 
5532a1795fSJyri Sarha 	drm_crtc_vblank_put(&tcrtc->crtc);
5632a1795fSJyri Sarha }
5732a1795fSJyri Sarha 
tidss_crtc_vblank_irq(struct drm_crtc * crtc)5832a1795fSJyri Sarha void tidss_crtc_vblank_irq(struct drm_crtc *crtc)
5932a1795fSJyri Sarha {
6032a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
6132a1795fSJyri Sarha 
6232a1795fSJyri Sarha 	drm_crtc_handle_vblank(crtc);
6332a1795fSJyri Sarha 
6432a1795fSJyri Sarha 	tidss_crtc_finish_page_flip(tcrtc);
6532a1795fSJyri Sarha }
6632a1795fSJyri Sarha 
tidss_crtc_framedone_irq(struct drm_crtc * crtc)6732a1795fSJyri Sarha void tidss_crtc_framedone_irq(struct drm_crtc *crtc)
6832a1795fSJyri Sarha {
6932a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
7032a1795fSJyri Sarha 
7132a1795fSJyri Sarha 	complete(&tcrtc->framedone_completion);
7232a1795fSJyri Sarha }
7332a1795fSJyri Sarha 
tidss_crtc_error_irq(struct drm_crtc * crtc,u64 irqstatus)7432a1795fSJyri Sarha void tidss_crtc_error_irq(struct drm_crtc *crtc, u64 irqstatus)
7532a1795fSJyri Sarha {
7632a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
7732a1795fSJyri Sarha 
7832a1795fSJyri Sarha 	dev_err_ratelimited(crtc->dev->dev, "CRTC%u SYNC LOST: (irq %llx)\n",
7932a1795fSJyri Sarha 			    tcrtc->hw_videoport, irqstatus);
8032a1795fSJyri Sarha }
8132a1795fSJyri Sarha 
8232a1795fSJyri Sarha /* drm_crtc_helper_funcs */
8332a1795fSJyri Sarha 
tidss_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)8432a1795fSJyri Sarha static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
8529b77ad7SMaxime Ripard 				   struct drm_atomic_state *state)
8632a1795fSJyri Sarha {
8729b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
8829b77ad7SMaxime Ripard 									  crtc);
8932a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
9002bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
9132a1795fSJyri Sarha 	struct dispc_device *dispc = tidss->dispc;
9232a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
9332a1795fSJyri Sarha 	u32 hw_videoport = tcrtc->hw_videoport;
9432a1795fSJyri Sarha 	const struct drm_display_mode *mode;
9532a1795fSJyri Sarha 	enum drm_mode_status ok;
9632a1795fSJyri Sarha 
9732a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
9832a1795fSJyri Sarha 
9929b77ad7SMaxime Ripard 	if (!crtc_state->enable)
10032a1795fSJyri Sarha 		return 0;
10132a1795fSJyri Sarha 
10229b77ad7SMaxime Ripard 	mode = &crtc_state->adjusted_mode;
10332a1795fSJyri Sarha 
10432a1795fSJyri Sarha 	ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
10532a1795fSJyri Sarha 	if (ok != MODE_OK) {
10632a1795fSJyri Sarha 		dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",
10732a1795fSJyri Sarha 			__func__, mode->hdisplay, mode->vdisplay, mode->clock);
10832a1795fSJyri Sarha 		return -EINVAL;
10932a1795fSJyri Sarha 	}
11032a1795fSJyri Sarha 
11129b77ad7SMaxime Ripard 	return dispc_vp_bus_check(dispc, hw_videoport, crtc_state);
11232a1795fSJyri Sarha }
11332a1795fSJyri Sarha 
114b33b5474SJyri Sarha /*
115b33b5474SJyri Sarha  * This needs all affected planes to be present in the atomic
116b33b5474SJyri Sarha  * state. The untouched planes are added to the state in
117b33b5474SJyri Sarha  * tidss_atomic_check().
118b33b5474SJyri Sarha  */
tidss_crtc_position_planes(struct tidss_device * tidss,struct drm_crtc * crtc,struct drm_crtc_state * old_state,bool newmodeset)119b33b5474SJyri Sarha static void tidss_crtc_position_planes(struct tidss_device *tidss,
120b33b5474SJyri Sarha 				       struct drm_crtc *crtc,
121b33b5474SJyri Sarha 				       struct drm_crtc_state *old_state,
122b33b5474SJyri Sarha 				       bool newmodeset)
123b33b5474SJyri Sarha {
124b33b5474SJyri Sarha 	struct drm_atomic_state *ostate = old_state->state;
125b33b5474SJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
126b33b5474SJyri Sarha 	struct drm_crtc_state *cstate = crtc->state;
127b33b5474SJyri Sarha 	int layer;
128b33b5474SJyri Sarha 
129b33b5474SJyri Sarha 	if (!newmodeset && !cstate->zpos_changed &&
130b33b5474SJyri Sarha 	    !to_tidss_crtc_state(cstate)->plane_pos_changed)
131b33b5474SJyri Sarha 		return;
132b33b5474SJyri Sarha 
133b33b5474SJyri Sarha 	for (layer = 0; layer < tidss->feat->num_planes; layer++) {
134b33b5474SJyri Sarha 		struct drm_plane_state *pstate;
135b33b5474SJyri Sarha 		struct drm_plane *plane;
136b33b5474SJyri Sarha 		bool layer_active = false;
137b33b5474SJyri Sarha 		int i;
138b33b5474SJyri Sarha 
139b33b5474SJyri Sarha 		for_each_new_plane_in_state(ostate, plane, pstate, i) {
140b33b5474SJyri Sarha 			if (pstate->crtc != crtc || !pstate->visible)
141b33b5474SJyri Sarha 				continue;
142b33b5474SJyri Sarha 
143b33b5474SJyri Sarha 			if (pstate->normalized_zpos == layer) {
144b33b5474SJyri Sarha 				layer_active = true;
145b33b5474SJyri Sarha 				break;
146b33b5474SJyri Sarha 			}
147b33b5474SJyri Sarha 		}
148b33b5474SJyri Sarha 
149b33b5474SJyri Sarha 		if (layer_active) {
150b33b5474SJyri Sarha 			struct tidss_plane *tplane = to_tidss_plane(plane);
151b33b5474SJyri Sarha 
152b33b5474SJyri Sarha 			dispc_ovr_set_plane(tidss->dispc, tplane->hw_plane_id,
153b33b5474SJyri Sarha 					    tcrtc->hw_videoport,
154b33b5474SJyri Sarha 					    pstate->crtc_x, pstate->crtc_y,
155b33b5474SJyri Sarha 					    layer);
156b33b5474SJyri Sarha 		}
157b33b5474SJyri Sarha 		dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer,
158b33b5474SJyri Sarha 				       layer_active);
159b33b5474SJyri Sarha 	}
160b33b5474SJyri Sarha }
161b33b5474SJyri Sarha 
tidss_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)16232a1795fSJyri Sarha static void tidss_crtc_atomic_flush(struct drm_crtc *crtc,
163f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
16432a1795fSJyri Sarha {
165f6ebe9f9SMaxime Ripard 	struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
166f6ebe9f9SMaxime Ripard 									      crtc);
16732a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
16832a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
16902bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
17032a1795fSJyri Sarha 	unsigned long flags;
17132a1795fSJyri Sarha 
172adc0787fSTomi Valkeinen 	dev_dbg(ddev->dev, "%s: %s is %sactive, %s modeset, event %p\n",
173adc0787fSTomi Valkeinen 		__func__, crtc->name, crtc->state->active ? "" : "not ",
174adc0787fSTomi Valkeinen 		drm_atomic_crtc_needs_modeset(crtc->state) ? "needs" : "doesn't need",
175adc0787fSTomi Valkeinen 		crtc->state->event);
17632a1795fSJyri Sarha 
17732a1795fSJyri Sarha 	/* There is nothing to do if CRTC is not going to be enabled. */
178adc0787fSTomi Valkeinen 	if (!crtc->state->active)
17932a1795fSJyri Sarha 		return;
18032a1795fSJyri Sarha 
18132a1795fSJyri Sarha 	/*
18232a1795fSJyri Sarha 	 * Flush CRTC changes with go bit only if new modeset is not
18332a1795fSJyri Sarha 	 * coming, so CRTC is enabled trough out the commit.
18432a1795fSJyri Sarha 	 */
18532a1795fSJyri Sarha 	if (drm_atomic_crtc_needs_modeset(crtc->state))
18632a1795fSJyri Sarha 		return;
18732a1795fSJyri Sarha 
18832a1795fSJyri Sarha 	/* If the GO bit is stuck we better quit here. */
18932a1795fSJyri Sarha 	if (WARN_ON(dispc_vp_go_busy(tidss->dispc, tcrtc->hw_videoport)))
19032a1795fSJyri Sarha 		return;
19132a1795fSJyri Sarha 
19232a1795fSJyri Sarha 	/* We should have event if CRTC is enabled through out this commit. */
19332a1795fSJyri Sarha 	if (WARN_ON(!crtc->state->event))
19432a1795fSJyri Sarha 		return;
19532a1795fSJyri Sarha 
19632a1795fSJyri Sarha 	/* Write vp properties to HW if needed. */
19732a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, false);
19832a1795fSJyri Sarha 
199b33b5474SJyri Sarha 	/* Update plane positions if needed. */
200b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_crtc_state, false);
201b33b5474SJyri Sarha 
20232a1795fSJyri Sarha 	WARN_ON(drm_crtc_vblank_get(crtc) != 0);
20332a1795fSJyri Sarha 
20432a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
20532a1795fSJyri Sarha 	dispc_vp_go(tidss->dispc, tcrtc->hw_videoport);
20632a1795fSJyri Sarha 
20732a1795fSJyri Sarha 	WARN_ON(tcrtc->event);
20832a1795fSJyri Sarha 
20932a1795fSJyri Sarha 	tcrtc->event = crtc->state->event;
21032a1795fSJyri Sarha 	crtc->state->event = NULL;
21132a1795fSJyri Sarha 
21232a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
21332a1795fSJyri Sarha }
21432a1795fSJyri Sarha 
tidss_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)21532a1795fSJyri Sarha static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,
216351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
21732a1795fSJyri Sarha {
218351f950dSMaxime Ripard 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
219351f950dSMaxime Ripard 									 crtc);
22032a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
22132a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
22202bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
22332a1795fSJyri Sarha 	const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
22432a1795fSJyri Sarha 	unsigned long flags;
22532a1795fSJyri Sarha 	int r;
22632a1795fSJyri Sarha 
22732a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
22832a1795fSJyri Sarha 
22932a1795fSJyri Sarha 	tidss_runtime_get(tidss);
23032a1795fSJyri Sarha 
23132a1795fSJyri Sarha 	r = dispc_vp_set_clk_rate(tidss->dispc, tcrtc->hw_videoport,
23232a1795fSJyri Sarha 				  mode->clock * 1000);
23332a1795fSJyri Sarha 	if (r != 0)
23432a1795fSJyri Sarha 		return;
23532a1795fSJyri Sarha 
23632a1795fSJyri Sarha 	r = dispc_vp_enable_clk(tidss->dispc, tcrtc->hw_videoport);
23732a1795fSJyri Sarha 	if (r != 0)
23832a1795fSJyri Sarha 		return;
23932a1795fSJyri Sarha 
24032a1795fSJyri Sarha 	dispc_vp_setup(tidss->dispc, tcrtc->hw_videoport, crtc->state, true);
241b33b5474SJyri Sarha 	tidss_crtc_position_planes(tidss, crtc, old_state, true);
24232a1795fSJyri Sarha 
24332a1795fSJyri Sarha 	/* Turn vertical blanking interrupt reporting on. */
24432a1795fSJyri Sarha 	drm_crtc_vblank_on(crtc);
24532a1795fSJyri Sarha 
24632a1795fSJyri Sarha 	dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24732a1795fSJyri Sarha 
24832a1795fSJyri Sarha 	dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state);
24932a1795fSJyri Sarha 
25032a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
25132a1795fSJyri Sarha 
25232a1795fSJyri Sarha 	if (crtc->state->event) {
25332a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
25432a1795fSJyri Sarha 		crtc->state->event = NULL;
25532a1795fSJyri Sarha 	}
25632a1795fSJyri Sarha 
25732a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
25832a1795fSJyri Sarha }
25932a1795fSJyri Sarha 
tidss_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)26032a1795fSJyri Sarha static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,
261351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
26232a1795fSJyri Sarha {
26332a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
26432a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
26502bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
26632a1795fSJyri Sarha 	unsigned long flags;
26732a1795fSJyri Sarha 
26832a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s, event %p\n", __func__, crtc->state->event);
26932a1795fSJyri Sarha 
27032a1795fSJyri Sarha 	reinit_completion(&tcrtc->framedone_completion);
27132a1795fSJyri Sarha 
272*1dce1ceeSTomi Valkeinen 	/*
273*1dce1ceeSTomi Valkeinen 	 * If a layer is left enabled when the videoport is disabled, and the
274*1dce1ceeSTomi Valkeinen 	 * vid pipeline that was used for the layer is taken into use on
275*1dce1ceeSTomi Valkeinen 	 * another videoport, the DSS will report sync lost issues. Disable all
276*1dce1ceeSTomi Valkeinen 	 * the layers here as a work-around.
277*1dce1ceeSTomi Valkeinen 	 */
278*1dce1ceeSTomi Valkeinen 	for (u32 layer = 0; layer < tidss->feat->num_planes; layer++)
279*1dce1ceeSTomi Valkeinen 		dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer,
280*1dce1ceeSTomi Valkeinen 				       false);
281*1dce1ceeSTomi Valkeinen 
28232a1795fSJyri Sarha 	dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport);
28332a1795fSJyri Sarha 
28432a1795fSJyri Sarha 	if (!wait_for_completion_timeout(&tcrtc->framedone_completion,
28532a1795fSJyri Sarha 					 msecs_to_jiffies(500)))
28632a1795fSJyri Sarha 		dev_err(tidss->dev, "Timeout waiting for framedone on crtc %d",
28732a1795fSJyri Sarha 			tcrtc->hw_videoport);
28832a1795fSJyri Sarha 
28932a1795fSJyri Sarha 	dispc_vp_unprepare(tidss->dispc, tcrtc->hw_videoport);
29032a1795fSJyri Sarha 
29132a1795fSJyri Sarha 	spin_lock_irqsave(&ddev->event_lock, flags);
29232a1795fSJyri Sarha 	if (crtc->state->event) {
29332a1795fSJyri Sarha 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
29432a1795fSJyri Sarha 		crtc->state->event = NULL;
29532a1795fSJyri Sarha 	}
29632a1795fSJyri Sarha 	spin_unlock_irqrestore(&ddev->event_lock, flags);
29732a1795fSJyri Sarha 
29832a1795fSJyri Sarha 	drm_crtc_vblank_off(crtc);
29932a1795fSJyri Sarha 
30032a1795fSJyri Sarha 	dispc_vp_disable_clk(tidss->dispc, tcrtc->hw_videoport);
30132a1795fSJyri Sarha 
30232a1795fSJyri Sarha 	tidss_runtime_put(tidss);
30332a1795fSJyri Sarha }
30432a1795fSJyri Sarha 
30532a1795fSJyri Sarha static
tidss_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)30632a1795fSJyri Sarha enum drm_mode_status tidss_crtc_mode_valid(struct drm_crtc *crtc,
30732a1795fSJyri Sarha 					   const struct drm_display_mode *mode)
30832a1795fSJyri Sarha {
30932a1795fSJyri Sarha 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
31032a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
31102bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
31232a1795fSJyri Sarha 
31332a1795fSJyri Sarha 	return dispc_vp_mode_valid(tidss->dispc, tcrtc->hw_videoport, mode);
31432a1795fSJyri Sarha }
31532a1795fSJyri Sarha 
31632a1795fSJyri Sarha static const struct drm_crtc_helper_funcs tidss_crtc_helper_funcs = {
31732a1795fSJyri Sarha 	.atomic_check = tidss_crtc_atomic_check,
31832a1795fSJyri Sarha 	.atomic_flush = tidss_crtc_atomic_flush,
31932a1795fSJyri Sarha 	.atomic_enable = tidss_crtc_atomic_enable,
32032a1795fSJyri Sarha 	.atomic_disable = tidss_crtc_atomic_disable,
32132a1795fSJyri Sarha 
32232a1795fSJyri Sarha 	.mode_valid = tidss_crtc_mode_valid,
32332a1795fSJyri Sarha };
32432a1795fSJyri Sarha 
32532a1795fSJyri Sarha /* drm_crtc_funcs */
32632a1795fSJyri Sarha 
tidss_crtc_enable_vblank(struct drm_crtc * crtc)32732a1795fSJyri Sarha static int tidss_crtc_enable_vblank(struct drm_crtc *crtc)
32832a1795fSJyri Sarha {
32932a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
33002bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
33132a1795fSJyri Sarha 
33232a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
33332a1795fSJyri Sarha 
33432a1795fSJyri Sarha 	tidss_runtime_get(tidss);
33532a1795fSJyri Sarha 
33632a1795fSJyri Sarha 	tidss_irq_enable_vblank(crtc);
33732a1795fSJyri Sarha 
33832a1795fSJyri Sarha 	return 0;
33932a1795fSJyri Sarha }
34032a1795fSJyri Sarha 
tidss_crtc_disable_vblank(struct drm_crtc * crtc)34132a1795fSJyri Sarha static void tidss_crtc_disable_vblank(struct drm_crtc *crtc)
34232a1795fSJyri Sarha {
34332a1795fSJyri Sarha 	struct drm_device *ddev = crtc->dev;
34402bb1317SDaniel Vetter 	struct tidss_device *tidss = to_tidss(ddev);
34532a1795fSJyri Sarha 
34632a1795fSJyri Sarha 	dev_dbg(ddev->dev, "%s\n", __func__);
34732a1795fSJyri Sarha 
34832a1795fSJyri Sarha 	tidss_irq_disable_vblank(crtc);
34932a1795fSJyri Sarha 
35032a1795fSJyri Sarha 	tidss_runtime_put(tidss);
35132a1795fSJyri Sarha }
35232a1795fSJyri Sarha 
tidss_crtc_reset(struct drm_crtc * crtc)35332a1795fSJyri Sarha static void tidss_crtc_reset(struct drm_crtc *crtc)
35432a1795fSJyri Sarha {
35532a1795fSJyri Sarha 	struct tidss_crtc_state *tcrtc;
35632a1795fSJyri Sarha 
35732a1795fSJyri Sarha 	if (crtc->state)
35832a1795fSJyri Sarha 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
35932a1795fSJyri Sarha 
36032a1795fSJyri Sarha 	kfree(crtc->state);
36132a1795fSJyri Sarha 
36232a1795fSJyri Sarha 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
36332a1795fSJyri Sarha 	if (!tcrtc) {
36432a1795fSJyri Sarha 		crtc->state = NULL;
36532a1795fSJyri Sarha 		return;
36632a1795fSJyri Sarha 	}
36732a1795fSJyri Sarha 
36851f644b4SDaniel Vetter 	__drm_atomic_helper_crtc_reset(crtc, &tcrtc->base);
36932a1795fSJyri Sarha }
37032a1795fSJyri Sarha 
tidss_crtc_duplicate_state(struct drm_crtc * crtc)37132a1795fSJyri Sarha static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
37232a1795fSJyri Sarha {
37332a1795fSJyri Sarha 	struct tidss_crtc_state *state, *current_state;
37432a1795fSJyri Sarha 
37532a1795fSJyri Sarha 	if (WARN_ON(!crtc->state))
37632a1795fSJyri Sarha 		return NULL;
37732a1795fSJyri Sarha 
37832a1795fSJyri Sarha 	current_state = to_tidss_crtc_state(crtc->state);
37932a1795fSJyri Sarha 
38032a1795fSJyri Sarha 	state = kmalloc(sizeof(*state), GFP_KERNEL);
38132a1795fSJyri Sarha 	if (!state)
38232a1795fSJyri Sarha 		return NULL;
38332a1795fSJyri Sarha 
38432a1795fSJyri Sarha 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
38532a1795fSJyri Sarha 
386b33b5474SJyri Sarha 	state->plane_pos_changed = false;
387b33b5474SJyri Sarha 
38832a1795fSJyri Sarha 	state->bus_format = current_state->bus_format;
38932a1795fSJyri Sarha 	state->bus_flags = current_state->bus_flags;
39032a1795fSJyri Sarha 
39132a1795fSJyri Sarha 	return &state->base;
39232a1795fSJyri Sarha }
39332a1795fSJyri Sarha 
tidss_crtc_destroy(struct drm_crtc * crtc)3949da67433STomi Valkeinen static void tidss_crtc_destroy(struct drm_crtc *crtc)
3959da67433STomi Valkeinen {
3969da67433STomi Valkeinen 	struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
3979da67433STomi Valkeinen 
3989da67433STomi Valkeinen 	drm_crtc_cleanup(crtc);
3999da67433STomi Valkeinen 	kfree(tcrtc);
4009da67433STomi Valkeinen }
4019da67433STomi Valkeinen 
40232a1795fSJyri Sarha static const struct drm_crtc_funcs tidss_crtc_funcs = {
40332a1795fSJyri Sarha 	.reset = tidss_crtc_reset,
4049da67433STomi Valkeinen 	.destroy = tidss_crtc_destroy,
40532a1795fSJyri Sarha 	.set_config = drm_atomic_helper_set_config,
40632a1795fSJyri Sarha 	.page_flip = drm_atomic_helper_page_flip,
40732a1795fSJyri Sarha 	.atomic_duplicate_state = tidss_crtc_duplicate_state,
40832a1795fSJyri Sarha 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
40932a1795fSJyri Sarha 	.enable_vblank = tidss_crtc_enable_vblank,
41032a1795fSJyri Sarha 	.disable_vblank = tidss_crtc_disable_vblank,
41132a1795fSJyri Sarha };
41232a1795fSJyri Sarha 
tidss_crtc_create(struct tidss_device * tidss,u32 hw_videoport,struct drm_plane * primary)41332a1795fSJyri Sarha struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,
41432a1795fSJyri Sarha 				     u32 hw_videoport,
41532a1795fSJyri Sarha 				     struct drm_plane *primary)
41632a1795fSJyri Sarha {
41732a1795fSJyri Sarha 	struct tidss_crtc *tcrtc;
41832a1795fSJyri Sarha 	struct drm_crtc *crtc;
41932a1795fSJyri Sarha 	unsigned int gamma_lut_size = 0;
42032a1795fSJyri Sarha 	bool has_ctm = tidss->feat->vp_feat.color.has_ctm;
42132a1795fSJyri Sarha 	int ret;
42232a1795fSJyri Sarha 
4239da67433STomi Valkeinen 	tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
42432a1795fSJyri Sarha 	if (!tcrtc)
42532a1795fSJyri Sarha 		return ERR_PTR(-ENOMEM);
42632a1795fSJyri Sarha 
42732a1795fSJyri Sarha 	tcrtc->hw_videoport = hw_videoport;
42832a1795fSJyri Sarha 	init_completion(&tcrtc->framedone_completion);
42932a1795fSJyri Sarha 
43032a1795fSJyri Sarha 	crtc =  &tcrtc->crtc;
43132a1795fSJyri Sarha 
43232a1795fSJyri Sarha 	ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary,
43332a1795fSJyri Sarha 					NULL, &tidss_crtc_funcs, NULL);
4349da67433STomi Valkeinen 	if (ret < 0) {
4359da67433STomi Valkeinen 		kfree(tcrtc);
43632a1795fSJyri Sarha 		return ERR_PTR(ret);
4379da67433STomi Valkeinen 	}
43832a1795fSJyri Sarha 
43932a1795fSJyri Sarha 	drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs);
44032a1795fSJyri Sarha 
44132a1795fSJyri Sarha 	/*
44232a1795fSJyri Sarha 	 * The dispc gamma functions adapt to what ever size we ask
44332a1795fSJyri Sarha 	 * from it no matter what HW supports. X-server assumes 256
44432a1795fSJyri Sarha 	 * element gamma tables so lets use that.
44532a1795fSJyri Sarha 	 */
44632a1795fSJyri Sarha 	if (tidss->feat->vp_feat.color.gamma_size)
44732a1795fSJyri Sarha 		gamma_lut_size = 256;
44832a1795fSJyri Sarha 
44932a1795fSJyri Sarha 	drm_crtc_enable_color_mgmt(crtc, 0, has_ctm, gamma_lut_size);
45032a1795fSJyri Sarha 	if (gamma_lut_size)
45132a1795fSJyri Sarha 		drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size);
45232a1795fSJyri Sarha 
45332a1795fSJyri Sarha 	return tcrtc;
45432a1795fSJyri Sarha }
455