2 * drivers/gpu/drm/omapdrm/omap_irq.c
4 * Copyright (C) 2012 Texas Instruments
5 * Author: Rob Clark <rob.clark@linaro.org>
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.
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
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/>.
22 static DEFINE_SPINLOCK(list_lock
);
24 static void omap_irq_error_handler(struct omap_drm_irq
*irq
,
27 DRM_ERROR("errors: %08x\n", irqstatus
);
30 /* call with list_lock and dispc runtime held */
31 static void omap_irq_update(struct drm_device
*dev
)
33 struct omap_drm_private
*priv
= dev
->dev_private
;
34 struct omap_drm_irq
*irq
;
35 uint32_t irqmask
= priv
->vblank_mask
;
37 assert_spin_locked(&list_lock
);
39 list_for_each_entry(irq
, &priv
->irq_list
, node
)
40 irqmask
|= irq
->irqmask
;
42 DBG("irqmask=%08x", irqmask
);
44 dispc_write_irqenable(irqmask
);
45 dispc_read_irqenable(); /* flush posted write */
48 void __omap_irq_register(struct drm_device
*dev
, struct omap_drm_irq
*irq
)
50 struct omap_drm_private
*priv
= dev
->dev_private
;
53 spin_lock_irqsave(&list_lock
, flags
);
55 if (!WARN_ON(irq
->registered
)) {
56 irq
->registered
= true;
57 list_add(&irq
->node
, &priv
->irq_list
);
61 spin_unlock_irqrestore(&list_lock
, flags
);
64 void omap_irq_register(struct drm_device
*dev
, struct omap_drm_irq
*irq
)
68 __omap_irq_register(dev
, irq
);
73 void __omap_irq_unregister(struct drm_device
*dev
, struct omap_drm_irq
*irq
)
77 spin_lock_irqsave(&list_lock
, flags
);
79 if (!WARN_ON(!irq
->registered
)) {
80 irq
->registered
= false;
85 spin_unlock_irqrestore(&list_lock
, flags
);
88 void omap_irq_unregister(struct drm_device
*dev
, struct omap_drm_irq
*irq
)
92 __omap_irq_unregister(dev
, irq
);
97 struct omap_irq_wait
{
98 struct omap_drm_irq irq
;
102 static DECLARE_WAIT_QUEUE_HEAD(wait_event
);
104 static void wait_irq(struct omap_drm_irq
*irq
, uint32_t irqstatus
)
106 struct omap_irq_wait
*wait
=
107 container_of(irq
, struct omap_irq_wait
, irq
);
109 wake_up_all(&wait_event
);
112 struct omap_irq_wait
* omap_irq_wait_init(struct drm_device
*dev
,
113 uint32_t irqmask
, int count
)
115 struct omap_irq_wait
*wait
= kzalloc(sizeof(*wait
), GFP_KERNEL
);
116 wait
->irq
.irq
= wait_irq
;
117 wait
->irq
.irqmask
= irqmask
;
119 omap_irq_register(dev
, &wait
->irq
);
123 int omap_irq_wait(struct drm_device
*dev
, struct omap_irq_wait
*wait
,
124 unsigned long timeout
)
126 int ret
= wait_event_timeout(wait_event
, (wait
->count
<= 0), timeout
);
127 omap_irq_unregister(dev
, &wait
->irq
);
135 * enable_vblank - enable vblank interrupt events
137 * @crtc: which irq to enable
139 * Enable vblank interrupts for @crtc. If the device doesn't have
140 * a hardware vblank counter, this routine should be a no-op, since
141 * interrupts will have to stay on to keep the count accurate.
144 * Zero on success, appropriate errno if the given @crtc's vblank
145 * interrupt cannot be enabled.
147 int omap_irq_enable_vblank(struct drm_device
*dev
, int crtc_id
)
149 struct omap_drm_private
*priv
= dev
->dev_private
;
150 struct drm_crtc
*crtc
= priv
->crtcs
[crtc_id
];
153 DBG("dev=%p, crtc=%d", dev
, crtc_id
);
155 spin_lock_irqsave(&list_lock
, flags
);
156 priv
->vblank_mask
|= pipe2vbl(crtc
);
157 omap_irq_update(dev
);
158 spin_unlock_irqrestore(&list_lock
, flags
);
164 * disable_vblank - disable vblank interrupt events
166 * @crtc: which irq to enable
168 * Disable vblank interrupts for @crtc. If the device doesn't have
169 * a hardware vblank counter, this routine should be a no-op, since
170 * interrupts will have to stay on to keep the count accurate.
172 void omap_irq_disable_vblank(struct drm_device
*dev
, int crtc_id
)
174 struct omap_drm_private
*priv
= dev
->dev_private
;
175 struct drm_crtc
*crtc
= priv
->crtcs
[crtc_id
];
178 DBG("dev=%p, crtc=%d", dev
, crtc_id
);
180 spin_lock_irqsave(&list_lock
, flags
);
181 priv
->vblank_mask
&= ~pipe2vbl(crtc
);
182 omap_irq_update(dev
);
183 spin_unlock_irqrestore(&list_lock
, flags
);
186 static irqreturn_t
omap_irq_handler(int irq
, void *arg
)
188 struct drm_device
*dev
= (struct drm_device
*) arg
;
189 struct omap_drm_private
*priv
= dev
->dev_private
;
190 struct omap_drm_irq
*handler
, *n
;
195 irqstatus
= dispc_read_irqstatus();
196 dispc_clear_irqstatus(irqstatus
);
197 dispc_read_irqstatus(); /* flush posted write */
199 VERB("irqs: %08x", irqstatus
);
201 for (id
= 0; id
< priv
->num_crtcs
; id
++) {
202 struct drm_crtc
*crtc
= priv
->crtcs
[id
];
204 if (irqstatus
& pipe2vbl(crtc
))
205 drm_handle_vblank(dev
, id
);
208 spin_lock_irqsave(&list_lock
, flags
);
209 list_for_each_entry_safe(handler
, n
, &priv
->irq_list
, node
) {
210 if (handler
->irqmask
& irqstatus
) {
211 spin_unlock_irqrestore(&list_lock
, flags
);
212 handler
->irq(handler
, handler
->irqmask
& irqstatus
);
213 spin_lock_irqsave(&list_lock
, flags
);
216 spin_unlock_irqrestore(&list_lock
, flags
);
222 * We need a special version, instead of just using drm_irq_install(),
223 * because we need to register the irq via omapdss. Once omapdss and
224 * omapdrm are merged together we can assign the dispc hwmod data to
225 * ourselves and drop these and just use drm_irq_{install,uninstall}()
228 int omap_drm_irq_install(struct drm_device
*dev
)
230 struct omap_drm_private
*priv
= dev
->dev_private
;
231 struct omap_drm_irq
*error_handler
= &priv
->error_handler
;
234 INIT_LIST_HEAD(&priv
->irq_list
);
237 dispc_clear_irqstatus(0xffffffff);
240 ret
= dispc_request_irq(omap_irq_handler
, dev
);
244 error_handler
->irq
= omap_irq_error_handler
;
245 error_handler
->irqmask
= DISPC_IRQ_OCP_ERR
;
247 /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think
248 * we just need to ignore it while enabling tv-out
250 error_handler
->irqmask
&= ~DISPC_IRQ_SYNC_LOST_DIGIT
;
252 omap_irq_register(dev
, error_handler
);
254 dev
->irq_enabled
= true;
259 void omap_drm_irq_uninstall(struct drm_device
*dev
)
261 unsigned long irqflags
;
264 if (!dev
->irq_enabled
)
267 dev
->irq_enabled
= false;
269 /* Wake up any waiters so they don't hang. */
270 if (dev
->num_crtcs
) {
271 spin_lock_irqsave(&dev
->vbl_lock
, irqflags
);
272 for (i
= 0; i
< dev
->num_crtcs
; i
++) {
273 wake_up(&dev
->vblank
[i
].queue
);
274 dev
->vblank
[i
].enabled
= false;
275 dev
->vblank
[i
].last
=
276 dev
->driver
->get_vblank_counter(dev
, i
);
278 spin_unlock_irqrestore(&dev
->vbl_lock
, irqflags
);