xref: /openbmc/linux/drivers/gpu/drm/omapdrm/omap_irq.c (revision 5f994ce5)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28bb0daffSRob Clark /*
31b409fdaSAlexander A. Klimov  * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
48bb0daffSRob Clark  * Author: Rob Clark <rob.clark@linaro.org>
58bb0daffSRob Clark  */
68bb0daffSRob Clark 
781f6156cSSam Ravnborg #include <drm/drm_vblank.h>
881f6156cSSam Ravnborg 
98bb0daffSRob Clark #include "omap_drv.h"
108bb0daffSRob Clark 
1180f91bffSLaurent Pinchart struct omap_irq_wait {
1280f91bffSLaurent Pinchart 	struct list_head node;
1384e1d457SLaurent Pinchart 	wait_queue_head_t wq;
14dfe9cfccSLaurent Pinchart 	u32 irqmask;
1580f91bffSLaurent Pinchart 	int count;
1680f91bffSLaurent Pinchart };
1780f91bffSLaurent Pinchart 
1884e1d457SLaurent Pinchart /* call with wait_lock and dispc runtime held */
omap_irq_update(struct drm_device * dev)198bb0daffSRob Clark static void omap_irq_update(struct drm_device *dev)
208bb0daffSRob Clark {
218bb0daffSRob Clark 	struct omap_drm_private *priv = dev->dev_private;
2280f91bffSLaurent Pinchart 	struct omap_irq_wait *wait;
23dfe9cfccSLaurent Pinchart 	u32 irqmask = priv->irq_mask;
248bb0daffSRob Clark 
2584e1d457SLaurent Pinchart 	assert_spin_locked(&priv->wait_lock);
268bb0daffSRob Clark 
2780f91bffSLaurent Pinchart 	list_for_each_entry(wait, &priv->wait_list, node)
2880f91bffSLaurent Pinchart 		irqmask |= wait->irqmask;
298bb0daffSRob Clark 
308bb0daffSRob Clark 	DBG("irqmask=%08x", irqmask);
318bb0daffSRob Clark 
32dac62bcaSTomi Valkeinen 	dispc_write_irqenable(priv->dispc, irqmask);
338bb0daffSRob Clark }
348bb0daffSRob Clark 
omap_irq_wait_handler(struct omap_irq_wait * wait)3580f91bffSLaurent Pinchart static void omap_irq_wait_handler(struct omap_irq_wait *wait)
368bb0daffSRob Clark {
378bb0daffSRob Clark 	wait->count--;
3884e1d457SLaurent Pinchart 	wake_up(&wait->wq);
398bb0daffSRob Clark }
408bb0daffSRob Clark 
omap_irq_wait_init(struct drm_device * dev,u32 irqmask,int count)418bb0daffSRob Clark struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
42dfe9cfccSLaurent Pinchart 		u32 irqmask, int count)
438bb0daffSRob Clark {
4480f91bffSLaurent Pinchart 	struct omap_drm_private *priv = dev->dev_private;
458bb0daffSRob Clark 	struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
4680f91bffSLaurent Pinchart 	unsigned long flags;
4780f91bffSLaurent Pinchart 
4884e1d457SLaurent Pinchart 	init_waitqueue_head(&wait->wq);
4980f91bffSLaurent Pinchart 	wait->irqmask = irqmask;
508bb0daffSRob Clark 	wait->count = count;
5180f91bffSLaurent Pinchart 
5284e1d457SLaurent Pinchart 	spin_lock_irqsave(&priv->wait_lock, flags);
5380f91bffSLaurent Pinchart 	list_add(&wait->node, &priv->wait_list);
5480f91bffSLaurent Pinchart 	omap_irq_update(dev);
5584e1d457SLaurent Pinchart 	spin_unlock_irqrestore(&priv->wait_lock, flags);
5680f91bffSLaurent Pinchart 
578bb0daffSRob Clark 	return wait;
588bb0daffSRob Clark }
598bb0daffSRob Clark 
omap_irq_wait(struct drm_device * dev,struct omap_irq_wait * wait,unsigned long timeout)608bb0daffSRob Clark int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
618bb0daffSRob Clark 		unsigned long timeout)
628bb0daffSRob Clark {
6384e1d457SLaurent Pinchart 	struct omap_drm_private *priv = dev->dev_private;
6480f91bffSLaurent Pinchart 	unsigned long flags;
6584e1d457SLaurent Pinchart 	int ret;
6680f91bffSLaurent Pinchart 
6784e1d457SLaurent Pinchart 	ret = wait_event_timeout(wait->wq, (wait->count <= 0), timeout);
6884e1d457SLaurent Pinchart 
6984e1d457SLaurent Pinchart 	spin_lock_irqsave(&priv->wait_lock, flags);
7080f91bffSLaurent Pinchart 	list_del(&wait->node);
7180f91bffSLaurent Pinchart 	omap_irq_update(dev);
7284e1d457SLaurent Pinchart 	spin_unlock_irqrestore(&priv->wait_lock, flags);
7380f91bffSLaurent Pinchart 
748bb0daffSRob Clark 	kfree(wait);
7580f91bffSLaurent Pinchart 
7680f91bffSLaurent Pinchart 	return ret == 0 ? -1 : 0;
778bb0daffSRob Clark }
788bb0daffSRob Clark 
omap_irq_enable_framedone(struct drm_crtc * crtc,bool enable)7947103a80SSebastian Reichel int omap_irq_enable_framedone(struct drm_crtc *crtc, bool enable)
8047103a80SSebastian Reichel {
8147103a80SSebastian Reichel 	struct drm_device *dev = crtc->dev;
8247103a80SSebastian Reichel 	struct omap_drm_private *priv = dev->dev_private;
8347103a80SSebastian Reichel 	unsigned long flags;
8447103a80SSebastian Reichel 	enum omap_channel channel = omap_crtc_channel(crtc);
8547103a80SSebastian Reichel 	int framedone_irq =
86dac62bcaSTomi Valkeinen 		dispc_mgr_get_framedone_irq(priv->dispc, channel);
8747103a80SSebastian Reichel 
8847103a80SSebastian Reichel 	DBG("dev=%p, crtc=%u, enable=%d", dev, channel, enable);
8947103a80SSebastian Reichel 
9047103a80SSebastian Reichel 	spin_lock_irqsave(&priv->wait_lock, flags);
9147103a80SSebastian Reichel 	if (enable)
9247103a80SSebastian Reichel 		priv->irq_mask |= framedone_irq;
9347103a80SSebastian Reichel 	else
9447103a80SSebastian Reichel 		priv->irq_mask &= ~framedone_irq;
9547103a80SSebastian Reichel 	omap_irq_update(dev);
9647103a80SSebastian Reichel 	spin_unlock_irqrestore(&priv->wait_lock, flags);
9747103a80SSebastian Reichel 
9847103a80SSebastian Reichel 	return 0;
9947103a80SSebastian Reichel }
10047103a80SSebastian Reichel 
1018bb0daffSRob Clark /**
102*5f994ce5STomi Valkeinen  * omap_irq_enable_vblank - enable vblank interrupt events
10367daa52dSLee Jones  * @crtc: DRM CRTC
1048bb0daffSRob Clark  *
1058bb0daffSRob Clark  * Enable vblank interrupts for @crtc.  If the device doesn't have
1068bb0daffSRob Clark  * a hardware vblank counter, this routine should be a no-op, since
1078bb0daffSRob Clark  * interrupts will have to stay on to keep the count accurate.
1088bb0daffSRob Clark  *
1098bb0daffSRob Clark  * RETURNS
1108bb0daffSRob Clark  * Zero on success, appropriate errno if the given @crtc's vblank
1118bb0daffSRob Clark  * interrupt cannot be enabled.
1128bb0daffSRob Clark  */
omap_irq_enable_vblank(struct drm_crtc * crtc)1130396162aSTomi Valkeinen int omap_irq_enable_vblank(struct drm_crtc *crtc)
1148bb0daffSRob Clark {
1150396162aSTomi Valkeinen 	struct drm_device *dev = crtc->dev;
1168bb0daffSRob Clark 	struct omap_drm_private *priv = dev->dev_private;
1178bb0daffSRob Clark 	unsigned long flags;
1180396162aSTomi Valkeinen 	enum omap_channel channel = omap_crtc_channel(crtc);
1198bb0daffSRob Clark 
1200396162aSTomi Valkeinen 	DBG("dev=%p, crtc=%u", dev, channel);
1218bb0daffSRob Clark 
12284e1d457SLaurent Pinchart 	spin_lock_irqsave(&priv->wait_lock, flags);
123dac62bcaSTomi Valkeinen 	priv->irq_mask |= dispc_mgr_get_vsync_irq(priv->dispc,
12450638ae5SLaurent Pinchart 							     channel);
1258bb0daffSRob Clark 	omap_irq_update(dev);
12684e1d457SLaurent Pinchart 	spin_unlock_irqrestore(&priv->wait_lock, flags);
1278bb0daffSRob Clark 
1288bb0daffSRob Clark 	return 0;
1298bb0daffSRob Clark }
1308bb0daffSRob Clark 
1318bb0daffSRob Clark /**
132*5f994ce5STomi Valkeinen  * omap_irq_disable_vblank - disable vblank interrupt events
13367daa52dSLee Jones  * @crtc: DRM CRTC
1348bb0daffSRob Clark  *
1358bb0daffSRob Clark  * Disable vblank interrupts for @crtc.  If the device doesn't have
1368bb0daffSRob Clark  * a hardware vblank counter, this routine should be a no-op, since
1378bb0daffSRob Clark  * interrupts will have to stay on to keep the count accurate.
1388bb0daffSRob Clark  */
omap_irq_disable_vblank(struct drm_crtc * crtc)1390396162aSTomi Valkeinen void omap_irq_disable_vblank(struct drm_crtc *crtc)
1408bb0daffSRob Clark {
1410396162aSTomi Valkeinen 	struct drm_device *dev = crtc->dev;
1428bb0daffSRob Clark 	struct omap_drm_private *priv = dev->dev_private;
1438bb0daffSRob Clark 	unsigned long flags;
1440396162aSTomi Valkeinen 	enum omap_channel channel = omap_crtc_channel(crtc);
1458bb0daffSRob Clark 
1460396162aSTomi Valkeinen 	DBG("dev=%p, crtc=%u", dev, channel);
1478bb0daffSRob Clark 
14884e1d457SLaurent Pinchart 	spin_lock_irqsave(&priv->wait_lock, flags);
149dac62bcaSTomi Valkeinen 	priv->irq_mask &= ~dispc_mgr_get_vsync_irq(priv->dispc,
15050638ae5SLaurent Pinchart 							      channel);
1518bb0daffSRob Clark 	omap_irq_update(dev);
15284e1d457SLaurent Pinchart 	spin_unlock_irqrestore(&priv->wait_lock, flags);
1538bb0daffSRob Clark }
1548bb0daffSRob Clark 
omap_irq_fifo_underflow(struct omap_drm_private * priv,u32 irqstatus)155728ae8ddSLaurent Pinchart static void omap_irq_fifo_underflow(struct omap_drm_private *priv,
156728ae8ddSLaurent Pinchart 				    u32 irqstatus)
157728ae8ddSLaurent Pinchart {
158728ae8ddSLaurent Pinchart 	static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
159728ae8ddSLaurent Pinchart 				      DEFAULT_RATELIMIT_BURST);
160728ae8ddSLaurent Pinchart 	static const struct {
161728ae8ddSLaurent Pinchart 		const char *name;
162728ae8ddSLaurent Pinchart 		u32 mask;
163728ae8ddSLaurent Pinchart 	} sources[] = {
164728ae8ddSLaurent Pinchart 		{ "gfx", DISPC_IRQ_GFX_FIFO_UNDERFLOW },
165728ae8ddSLaurent Pinchart 		{ "vid1", DISPC_IRQ_VID1_FIFO_UNDERFLOW },
166728ae8ddSLaurent Pinchart 		{ "vid2", DISPC_IRQ_VID2_FIFO_UNDERFLOW },
167728ae8ddSLaurent Pinchart 		{ "vid3", DISPC_IRQ_VID3_FIFO_UNDERFLOW },
168728ae8ddSLaurent Pinchart 	};
169728ae8ddSLaurent Pinchart 
170728ae8ddSLaurent Pinchart 	const u32 mask = DISPC_IRQ_GFX_FIFO_UNDERFLOW
171728ae8ddSLaurent Pinchart 		       | DISPC_IRQ_VID1_FIFO_UNDERFLOW
172728ae8ddSLaurent Pinchart 		       | DISPC_IRQ_VID2_FIFO_UNDERFLOW
173728ae8ddSLaurent Pinchart 		       | DISPC_IRQ_VID3_FIFO_UNDERFLOW;
174728ae8ddSLaurent Pinchart 	unsigned int i;
175728ae8ddSLaurent Pinchart 
17684e1d457SLaurent Pinchart 	spin_lock(&priv->wait_lock);
177728ae8ddSLaurent Pinchart 	irqstatus &= priv->irq_mask & mask;
17884e1d457SLaurent Pinchart 	spin_unlock(&priv->wait_lock);
179728ae8ddSLaurent Pinchart 
180728ae8ddSLaurent Pinchart 	if (!irqstatus)
181728ae8ddSLaurent Pinchart 		return;
182728ae8ddSLaurent Pinchart 
183728ae8ddSLaurent Pinchart 	if (!__ratelimit(&_rs))
184728ae8ddSLaurent Pinchart 		return;
185728ae8ddSLaurent Pinchart 
186728ae8ddSLaurent Pinchart 	DRM_ERROR("FIFO underflow on ");
187728ae8ddSLaurent Pinchart 
188728ae8ddSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(sources); ++i) {
189728ae8ddSLaurent Pinchart 		if (sources[i].mask & irqstatus)
190728ae8ddSLaurent Pinchart 			pr_cont("%s ", sources[i].name);
191728ae8ddSLaurent Pinchart 	}
192728ae8ddSLaurent Pinchart 
193728ae8ddSLaurent Pinchart 	pr_cont("(0x%08x)\n", irqstatus);
194728ae8ddSLaurent Pinchart }
195728ae8ddSLaurent Pinchart 
omap_irq_ocp_error_handler(struct drm_device * dev,u32 irqstatus)196dc50be89STomi Valkeinen static void omap_irq_ocp_error_handler(struct drm_device *dev,
197dc50be89STomi Valkeinen 	u32 irqstatus)
1986b5538d4SLaurent Pinchart {
1996b5538d4SLaurent Pinchart 	if (!(irqstatus & DISPC_IRQ_OCP_ERR))
2006b5538d4SLaurent Pinchart 		return;
2016b5538d4SLaurent Pinchart 
202dc50be89STomi Valkeinen 	dev_err_ratelimited(dev->dev, "OCP error\n");
2036b5538d4SLaurent Pinchart }
2046b5538d4SLaurent Pinchart 
omap_irq_handler(int irq,void * arg)205f13ab005SLaurent Pinchart static irqreturn_t omap_irq_handler(int irq, void *arg)
2068bb0daffSRob Clark {
2078bb0daffSRob Clark 	struct drm_device *dev = (struct drm_device *) arg;
2088bb0daffSRob Clark 	struct omap_drm_private *priv = dev->dev_private;
20980f91bffSLaurent Pinchart 	struct omap_irq_wait *wait, *n;
2108bb0daffSRob Clark 	unsigned long flags;
2118bb0daffSRob Clark 	unsigned int id;
2128bb0daffSRob Clark 	u32 irqstatus;
2138bb0daffSRob Clark 
214dac62bcaSTomi Valkeinen 	irqstatus = dispc_read_irqstatus(priv->dispc);
215dac62bcaSTomi Valkeinen 	dispc_clear_irqstatus(priv->dispc, irqstatus);
216dac62bcaSTomi Valkeinen 	dispc_read_irqstatus(priv->dispc);	/* flush posted write */
2178bb0daffSRob Clark 
2188bb0daffSRob Clark 	VERB("irqs: %08x", irqstatus);
2198bb0daffSRob Clark 
2202ee76792SLaurent Pinchart 	for (id = 0; id < priv->num_pipes; id++) {
2212ee76792SLaurent Pinchart 		struct drm_crtc *crtc = priv->pipes[id].crtc;
222e0519af7SLaurent Pinchart 		enum omap_channel channel = omap_crtc_channel(crtc);
2230d8f371fSArchit Taneja 
224dac62bcaSTomi Valkeinen 		if (irqstatus & dispc_mgr_get_vsync_irq(priv->dispc, channel)) {
2258bb0daffSRob Clark 			drm_handle_vblank(dev, id);
22614389a37SLaurent Pinchart 			omap_crtc_vblank_irq(crtc);
22714389a37SLaurent Pinchart 		}
228e0519af7SLaurent Pinchart 
229dac62bcaSTomi Valkeinen 		if (irqstatus & dispc_mgr_get_sync_lost_irq(priv->dispc, channel))
230e0519af7SLaurent Pinchart 			omap_crtc_error_irq(crtc, irqstatus);
23147103a80SSebastian Reichel 
232dac62bcaSTomi Valkeinen 		if (irqstatus & dispc_mgr_get_framedone_irq(priv->dispc, channel))
23347103a80SSebastian Reichel 			omap_crtc_framedone_irq(crtc, irqstatus);
2340d8f371fSArchit Taneja 	}
2358bb0daffSRob Clark 
236dc50be89STomi Valkeinen 	omap_irq_ocp_error_handler(dev, irqstatus);
237728ae8ddSLaurent Pinchart 	omap_irq_fifo_underflow(priv, irqstatus);
238728ae8ddSLaurent Pinchart 
23984e1d457SLaurent Pinchart 	spin_lock_irqsave(&priv->wait_lock, flags);
24080f91bffSLaurent Pinchart 	list_for_each_entry_safe(wait, n, &priv->wait_list, node) {
24180f91bffSLaurent Pinchart 		if (wait->irqmask & irqstatus)
24280f91bffSLaurent Pinchart 			omap_irq_wait_handler(wait);
2438bb0daffSRob Clark 	}
24484e1d457SLaurent Pinchart 	spin_unlock_irqrestore(&priv->wait_lock, flags);
2458bb0daffSRob Clark 
2468bb0daffSRob Clark 	return IRQ_HANDLED;
2478bb0daffSRob Clark }
2488bb0daffSRob Clark 
249728ae8ddSLaurent Pinchart static const u32 omap_underflow_irqs[] = {
250728ae8ddSLaurent Pinchart 	[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
251728ae8ddSLaurent Pinchart 	[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
252728ae8ddSLaurent Pinchart 	[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
253728ae8ddSLaurent Pinchart 	[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
254728ae8ddSLaurent Pinchart };
255728ae8ddSLaurent Pinchart 
omap_drm_irq_install(struct drm_device * dev)256f13ab005SLaurent Pinchart int omap_drm_irq_install(struct drm_device *dev)
2578bb0daffSRob Clark {
2588bb0daffSRob Clark 	struct omap_drm_private *priv = dev->dev_private;
259dac62bcaSTomi Valkeinen 	unsigned int num_mgrs = dispc_get_num_mgrs(priv->dispc);
260728ae8ddSLaurent Pinchart 	unsigned int max_planes;
261728ae8ddSLaurent Pinchart 	unsigned int i;
262f13ab005SLaurent Pinchart 	int ret;
2638bb0daffSRob Clark 
26484e1d457SLaurent Pinchart 	spin_lock_init(&priv->wait_lock);
26580f91bffSLaurent Pinchart 	INIT_LIST_HEAD(&priv->wait_list);
2668bb0daffSRob Clark 
2676b5538d4SLaurent Pinchart 	priv->irq_mask = DISPC_IRQ_OCP_ERR;
268728ae8ddSLaurent Pinchart 
269728ae8ddSLaurent Pinchart 	max_planes = min(ARRAY_SIZE(priv->planes),
270728ae8ddSLaurent Pinchart 			 ARRAY_SIZE(omap_underflow_irqs));
271728ae8ddSLaurent Pinchart 	for (i = 0; i < max_planes; ++i) {
272728ae8ddSLaurent Pinchart 		if (priv->planes[i])
273728ae8ddSLaurent Pinchart 			priv->irq_mask |= omap_underflow_irqs[i];
274728ae8ddSLaurent Pinchart 	}
275728ae8ddSLaurent Pinchart 
276e0519af7SLaurent Pinchart 	for (i = 0; i < num_mgrs; ++i)
277dac62bcaSTomi Valkeinen 		priv->irq_mask |= dispc_mgr_get_sync_lost_irq(priv->dispc, i);
278e0519af7SLaurent Pinchart 
279dac62bcaSTomi Valkeinen 	dispc_runtime_get(priv->dispc);
280dac62bcaSTomi Valkeinen 	dispc_clear_irqstatus(priv->dispc, 0xffffffff);
281dac62bcaSTomi Valkeinen 	dispc_runtime_put(priv->dispc);
282f13ab005SLaurent Pinchart 
283dac62bcaSTomi Valkeinen 	ret = dispc_request_irq(priv->dispc, omap_irq_handler, dev);
284f13ab005SLaurent Pinchart 	if (ret < 0)
285f13ab005SLaurent Pinchart 		return ret;
286f13ab005SLaurent Pinchart 
287daea7501SThomas Zimmermann 	priv->irq_enabled = true;
288f13ab005SLaurent Pinchart 
2898bb0daffSRob Clark 	return 0;
2908bb0daffSRob Clark }
2918bb0daffSRob Clark 
omap_drm_irq_uninstall(struct drm_device * dev)292f13ab005SLaurent Pinchart void omap_drm_irq_uninstall(struct drm_device *dev)
2938bb0daffSRob Clark {
2949f759225STomi Valkeinen 	struct omap_drm_private *priv = dev->dev_private;
2958bb0daffSRob Clark 
296daea7501SThomas Zimmermann 	if (!priv->irq_enabled)
297f13ab005SLaurent Pinchart 		return;
2988bb0daffSRob Clark 
299daea7501SThomas Zimmermann 	priv->irq_enabled = false;
300f13ab005SLaurent Pinchart 
301dac62bcaSTomi Valkeinen 	dispc_free_irq(priv->dispc, dev);
3028bb0daffSRob Clark }
303