blob: 0ef2d609653e9235e741f78122e20c0be5281a2b [file] [log] [blame]
Rob Clarkf5f94542012-12-04 13:59:12 -06001/*
Rob Clark8bb0daf2013-02-11 12:43:09 -05002 * drivers/gpu/drm/omapdrm/omap_irq.c
Rob Clarkf5f94542012-12-04 13:59:12 -06003 *
4 * Copyright (C) 2012 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22static DEFINE_SPINLOCK(list_lock);
23
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030024struct omap_irq_wait {
25 struct list_head node;
26 uint32_t irqmask;
27 int count;
28};
29
Rob Clarkf5f94542012-12-04 13:59:12 -060030/* call with list_lock and dispc runtime held */
31static void omap_irq_update(struct drm_device *dev)
32{
33 struct omap_drm_private *priv = dev->dev_private;
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030034 struct omap_irq_wait *wait;
Laurent Pinchart728ae8d2015-05-28 00:21:29 +030035 uint32_t irqmask = priv->irq_mask;
Rob Clarkf5f94542012-12-04 13:59:12 -060036
Tomi Valkeinen8519c622014-11-28 14:34:16 +020037 assert_spin_locked(&list_lock);
Rob Clarkf5f94542012-12-04 13:59:12 -060038
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030039 list_for_each_entry(wait, &priv->wait_list, node)
40 irqmask |= wait->irqmask;
Rob Clarkf5f94542012-12-04 13:59:12 -060041
42 DBG("irqmask=%08x", irqmask);
43
44 dispc_write_irqenable(irqmask);
45 dispc_read_irqenable(); /* flush posted write */
46}
47
Rob Clarkf5f94542012-12-04 13:59:12 -060048static DECLARE_WAIT_QUEUE_HEAD(wait_event);
49
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030050static void omap_irq_wait_handler(struct omap_irq_wait *wait)
Rob Clarkf5f94542012-12-04 13:59:12 -060051{
Rob Clarkf5f94542012-12-04 13:59:12 -060052 wait->count--;
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030053 wake_up(&wait_event);
Rob Clarkf5f94542012-12-04 13:59:12 -060054}
55
56struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
57 uint32_t irqmask, int count)
58{
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030059 struct omap_drm_private *priv = dev->dev_private;
Rob Clarkf5f94542012-12-04 13:59:12 -060060 struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030061 unsigned long flags;
62
63 wait->irqmask = irqmask;
Rob Clarkf5f94542012-12-04 13:59:12 -060064 wait->count = count;
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030065
66 spin_lock_irqsave(&list_lock, flags);
67 list_add(&wait->node, &priv->wait_list);
68 omap_irq_update(dev);
69 spin_unlock_irqrestore(&list_lock, flags);
70
Rob Clarkf5f94542012-12-04 13:59:12 -060071 return wait;
72}
73
74int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
75 unsigned long timeout)
76{
77 int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout);
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030078 unsigned long flags;
79
80 spin_lock_irqsave(&list_lock, flags);
81 list_del(&wait->node);
82 omap_irq_update(dev);
83 spin_unlock_irqrestore(&list_lock, flags);
84
Rob Clarkf5f94542012-12-04 13:59:12 -060085 kfree(wait);
Laurent Pinchart80f91bf2016-04-19 02:47:02 +030086
87 return ret == 0 ? -1 : 0;
Rob Clarkf5f94542012-12-04 13:59:12 -060088}
89
90/**
91 * enable_vblank - enable vblank interrupt events
92 * @dev: DRM device
Thierry Reding88e72712015-09-24 18:35:31 +020093 * @pipe: which irq to enable
Rob Clarkf5f94542012-12-04 13:59:12 -060094 *
95 * Enable vblank interrupts for @crtc. If the device doesn't have
96 * a hardware vblank counter, this routine should be a no-op, since
97 * interrupts will have to stay on to keep the count accurate.
98 *
99 * RETURNS
100 * Zero on success, appropriate errno if the given @crtc's vblank
101 * interrupt cannot be enabled.
102 */
Thierry Reding88e72712015-09-24 18:35:31 +0200103int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe)
Rob Clarkf5f94542012-12-04 13:59:12 -0600104{
105 struct omap_drm_private *priv = dev->dev_private;
Thierry Reding88e72712015-09-24 18:35:31 +0200106 struct drm_crtc *crtc = priv->crtcs[pipe];
Rob Clarkf5f94542012-12-04 13:59:12 -0600107 unsigned long flags;
108
Thierry Reding88e72712015-09-24 18:35:31 +0200109 DBG("dev=%p, crtc=%u", dev, pipe);
Rob Clarkf5f94542012-12-04 13:59:12 -0600110
Rob Clarkf5f94542012-12-04 13:59:12 -0600111 spin_lock_irqsave(&list_lock, flags);
Laurent Pinchartca52d2f2015-05-27 19:15:22 +0300112 priv->irq_mask |= dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc));
Rob Clarkf5f94542012-12-04 13:59:12 -0600113 omap_irq_update(dev);
114 spin_unlock_irqrestore(&list_lock, flags);
Rob Clarkf5f94542012-12-04 13:59:12 -0600115
116 return 0;
117}
118
119/**
120 * disable_vblank - disable vblank interrupt events
121 * @dev: DRM device
Thierry Reding88e72712015-09-24 18:35:31 +0200122 * @pipe: which irq to enable
Rob Clarkf5f94542012-12-04 13:59:12 -0600123 *
124 * Disable vblank interrupts for @crtc. If the device doesn't have
125 * a hardware vblank counter, this routine should be a no-op, since
126 * interrupts will have to stay on to keep the count accurate.
127 */
Thierry Reding88e72712015-09-24 18:35:31 +0200128void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe)
Rob Clarkf5f94542012-12-04 13:59:12 -0600129{
130 struct omap_drm_private *priv = dev->dev_private;
Thierry Reding88e72712015-09-24 18:35:31 +0200131 struct drm_crtc *crtc = priv->crtcs[pipe];
Rob Clarkf5f94542012-12-04 13:59:12 -0600132 unsigned long flags;
133
Thierry Reding88e72712015-09-24 18:35:31 +0200134 DBG("dev=%p, crtc=%u", dev, pipe);
Rob Clarkf5f94542012-12-04 13:59:12 -0600135
Rob Clarkf5f94542012-12-04 13:59:12 -0600136 spin_lock_irqsave(&list_lock, flags);
Laurent Pinchartca52d2f2015-05-27 19:15:22 +0300137 priv->irq_mask &= ~dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc));
Rob Clarkf5f94542012-12-04 13:59:12 -0600138 omap_irq_update(dev);
139 spin_unlock_irqrestore(&list_lock, flags);
Rob Clarkf5f94542012-12-04 13:59:12 -0600140}
141
Laurent Pinchart728ae8d2015-05-28 00:21:29 +0300142static void omap_irq_fifo_underflow(struct omap_drm_private *priv,
143 u32 irqstatus)
144{
145 static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
146 DEFAULT_RATELIMIT_BURST);
147 static const struct {
148 const char *name;
149 u32 mask;
150 } sources[] = {
151 { "gfx", DISPC_IRQ_GFX_FIFO_UNDERFLOW },
152 { "vid1", DISPC_IRQ_VID1_FIFO_UNDERFLOW },
153 { "vid2", DISPC_IRQ_VID2_FIFO_UNDERFLOW },
154 { "vid3", DISPC_IRQ_VID3_FIFO_UNDERFLOW },
155 };
156
157 const u32 mask = DISPC_IRQ_GFX_FIFO_UNDERFLOW
158 | DISPC_IRQ_VID1_FIFO_UNDERFLOW
159 | DISPC_IRQ_VID2_FIFO_UNDERFLOW
160 | DISPC_IRQ_VID3_FIFO_UNDERFLOW;
161 unsigned int i;
162
163 spin_lock(&list_lock);
164 irqstatus &= priv->irq_mask & mask;
165 spin_unlock(&list_lock);
166
167 if (!irqstatus)
168 return;
169
170 if (!__ratelimit(&_rs))
171 return;
172
173 DRM_ERROR("FIFO underflow on ");
174
175 for (i = 0; i < ARRAY_SIZE(sources); ++i) {
176 if (sources[i].mask & irqstatus)
177 pr_cont("%s ", sources[i].name);
178 }
179
180 pr_cont("(0x%08x)\n", irqstatus);
181}
182
Laurent Pinchart6b5538d2015-05-28 01:05:20 +0300183static void omap_irq_ocp_error_handler(u32 irqstatus)
184{
185 if (!(irqstatus & DISPC_IRQ_OCP_ERR))
186 return;
187
188 DRM_ERROR("OCP error\n");
189}
190
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200191static irqreturn_t omap_irq_handler(int irq, void *arg)
Rob Clarkf5f94542012-12-04 13:59:12 -0600192{
193 struct drm_device *dev = (struct drm_device *) arg;
194 struct omap_drm_private *priv = dev->dev_private;
Laurent Pinchart80f91bf2016-04-19 02:47:02 +0300195 struct omap_irq_wait *wait, *n;
Rob Clarkf5f94542012-12-04 13:59:12 -0600196 unsigned long flags;
197 unsigned int id;
198 u32 irqstatus;
199
200 irqstatus = dispc_read_irqstatus();
201 dispc_clear_irqstatus(irqstatus);
202 dispc_read_irqstatus(); /* flush posted write */
203
204 VERB("irqs: %08x", irqstatus);
205
Archit Taneja0d8f3712013-03-26 19:15:19 +0530206 for (id = 0; id < priv->num_crtcs; id++) {
207 struct drm_crtc *crtc = priv->crtcs[id];
Laurent Pincharte0519af2015-05-28 00:21:29 +0300208 enum omap_channel channel = omap_crtc_channel(crtc);
Archit Taneja0d8f3712013-03-26 19:15:19 +0530209
Laurent Pinchartca52d2f2015-05-27 19:15:22 +0300210 if (irqstatus & dispc_mgr_get_vsync_irq(channel)) {
Rob Clarkf5f94542012-12-04 13:59:12 -0600211 drm_handle_vblank(dev, id);
Laurent Pinchart14389a32016-04-19 01:43:03 +0300212 omap_crtc_vblank_irq(crtc);
213 }
Laurent Pincharte0519af2015-05-28 00:21:29 +0300214
215 if (irqstatus & dispc_mgr_get_sync_lost_irq(channel))
216 omap_crtc_error_irq(crtc, irqstatus);
Archit Taneja0d8f3712013-03-26 19:15:19 +0530217 }
Rob Clarkf5f94542012-12-04 13:59:12 -0600218
Laurent Pinchart6b5538d2015-05-28 01:05:20 +0300219 omap_irq_ocp_error_handler(irqstatus);
Laurent Pinchart728ae8d2015-05-28 00:21:29 +0300220 omap_irq_fifo_underflow(priv, irqstatus);
221
Rob Clarkf5f94542012-12-04 13:59:12 -0600222 spin_lock_irqsave(&list_lock, flags);
Laurent Pinchart80f91bf2016-04-19 02:47:02 +0300223 list_for_each_entry_safe(wait, n, &priv->wait_list, node) {
224 if (wait->irqmask & irqstatus)
225 omap_irq_wait_handler(wait);
Rob Clarkf5f94542012-12-04 13:59:12 -0600226 }
227 spin_unlock_irqrestore(&list_lock, flags);
228
229 return IRQ_HANDLED;
230}
231
Laurent Pinchart728ae8d2015-05-28 00:21:29 +0300232static const u32 omap_underflow_irqs[] = {
233 [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
234 [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
235 [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
236 [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
237};
238
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200239/*
240 * We need a special version, instead of just using drm_irq_install(),
241 * because we need to register the irq via omapdss. Once omapdss and
242 * omapdrm are merged together we can assign the dispc hwmod data to
243 * ourselves and drop these and just use drm_irq_{install,uninstall}()
244 */
Rob Clarkf5f94542012-12-04 13:59:12 -0600245
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200246int omap_drm_irq_install(struct drm_device *dev)
Rob Clarkf5f94542012-12-04 13:59:12 -0600247{
248 struct omap_drm_private *priv = dev->dev_private;
Laurent Pincharte0519af2015-05-28 00:21:29 +0300249 unsigned int num_mgrs = dss_feat_get_num_mgrs();
Laurent Pinchart728ae8d2015-05-28 00:21:29 +0300250 unsigned int max_planes;
251 unsigned int i;
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200252 int ret;
Rob Clarkf5f94542012-12-04 13:59:12 -0600253
Laurent Pinchart80f91bf2016-04-19 02:47:02 +0300254 INIT_LIST_HEAD(&priv->wait_list);
Rob Clarkf5f94542012-12-04 13:59:12 -0600255
Laurent Pinchart6b5538d2015-05-28 01:05:20 +0300256 priv->irq_mask = DISPC_IRQ_OCP_ERR;
Laurent Pinchart728ae8d2015-05-28 00:21:29 +0300257
258 max_planes = min(ARRAY_SIZE(priv->planes),
259 ARRAY_SIZE(omap_underflow_irqs));
260 for (i = 0; i < max_planes; ++i) {
261 if (priv->planes[i])
262 priv->irq_mask |= omap_underflow_irqs[i];
263 }
264
Laurent Pincharte0519af2015-05-28 00:21:29 +0300265 for (i = 0; i < num_mgrs; ++i)
266 priv->irq_mask |= dispc_mgr_get_sync_lost_irq(i);
267
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200268 dispc_runtime_get();
269 dispc_clear_irqstatus(0xffffffff);
270 dispc_runtime_put();
271
272 ret = dispc_request_irq(omap_irq_handler, dev);
273 if (ret < 0)
274 return ret;
275
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200276 dev->irq_enabled = true;
277
Rob Clarkf5f94542012-12-04 13:59:12 -0600278 return 0;
279}
280
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200281void omap_drm_irq_uninstall(struct drm_device *dev)
Rob Clarkf5f94542012-12-04 13:59:12 -0600282{
283 unsigned long irqflags;
Ville Syrjälä44238432013-10-04 14:53:37 +0300284 int i;
Rob Clarkf5f94542012-12-04 13:59:12 -0600285
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200286 if (!dev->irq_enabled)
287 return;
Rob Clarkf5f94542012-12-04 13:59:12 -0600288
Laurent Pinchartf13ab002015-01-25 22:06:45 +0200289 dev->irq_enabled = false;
290
291 /* Wake up any waiters so they don't hang. */
Rob Clarkf5f94542012-12-04 13:59:12 -0600292 if (dev->num_crtcs) {
293 spin_lock_irqsave(&dev->vbl_lock, irqflags);
294 for (i = 0; i < dev->num_crtcs; i++) {
Daniel Vetter57ed0f72013-12-11 11:34:43 +0100295 wake_up(&dev->vblank[i].queue);
Ville Syrjälä5380e922013-10-04 14:53:36 +0300296 dev->vblank[i].enabled = false;
297 dev->vblank[i].last =
Rob Clarkf5f94542012-12-04 13:59:12 -0600298 dev->driver->get_vblank_counter(dev, i);
299 }
300 spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
301 }
302
Rob Clarkf5f94542012-12-04 13:59:12 -0600303 dispc_free_irq(dev);
Rob Clarkf5f94542012-12-04 13:59:12 -0600304}