| /* | |
| * Copyright (C) 2013 Red Hat | |
| * Author: Rob Clark <robdclark@gmail.com> | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 as published by | |
| * the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
| * more details. | |
| * | |
| * You should have received a copy of the GNU General Public License along with | |
| * this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| #include "msm_drv.h" | |
| #include "mdp_kms.h" | |
| struct mdp_irq_wait { | |
| struct mdp_irq irq; | |
| int count; | |
| }; | |
| static DECLARE_WAIT_QUEUE_HEAD(wait_event); | |
| static DEFINE_SPINLOCK(list_lock); | |
| static void update_irq(struct mdp_kms *mdp_kms) | |
| { | |
| struct mdp_irq *irq; | |
| uint32_t irqmask = mdp_kms->vblank_mask; | |
| assert_spin_locked(&list_lock); | |
| list_for_each_entry(irq, &mdp_kms->irq_list, node) | |
| irqmask |= irq->irqmask; | |
| mdp_kms->funcs->set_irqmask(mdp_kms, irqmask, mdp_kms->cur_irq_mask); | |
| mdp_kms->cur_irq_mask = irqmask; | |
| } | |
| /* if an mdp_irq's irqmask has changed, such as when mdp5 crtc<->encoder | |
| * link changes, this must be called to figure out the new global irqmask | |
| */ | |
| void mdp_irq_update(struct mdp_kms *mdp_kms) | |
| { | |
| unsigned long flags; | |
| spin_lock_irqsave(&list_lock, flags); | |
| update_irq(mdp_kms); | |
| spin_unlock_irqrestore(&list_lock, flags); | |
| } | |
| void mdp_dispatch_irqs(struct mdp_kms *mdp_kms, uint32_t status) | |
| { | |
| struct mdp_irq *handler, *n; | |
| unsigned long flags; | |
| spin_lock_irqsave(&list_lock, flags); | |
| mdp_kms->in_irq = true; | |
| list_for_each_entry_safe(handler, n, &mdp_kms->irq_list, node) { | |
| if (handler->irqmask & status) { | |
| spin_unlock_irqrestore(&list_lock, flags); | |
| handler->irq(handler, handler->irqmask & status); | |
| spin_lock_irqsave(&list_lock, flags); | |
| } | |
| } | |
| mdp_kms->in_irq = false; | |
| update_irq(mdp_kms); | |
| spin_unlock_irqrestore(&list_lock, flags); | |
| } | |
| void mdp_update_vblank_mask(struct mdp_kms *mdp_kms, uint32_t mask, bool enable) | |
| { | |
| unsigned long flags; | |
| spin_lock_irqsave(&list_lock, flags); | |
| if (enable) | |
| mdp_kms->vblank_mask |= mask; | |
| else | |
| mdp_kms->vblank_mask &= ~mask; | |
| update_irq(mdp_kms); | |
| spin_unlock_irqrestore(&list_lock, flags); | |
| } | |
| static void wait_irq(struct mdp_irq *irq, uint32_t irqstatus) | |
| { | |
| struct mdp_irq_wait *wait = | |
| container_of(irq, struct mdp_irq_wait, irq); | |
| wait->count--; | |
| wake_up_all(&wait_event); | |
| } | |
| void mdp_irq_wait(struct mdp_kms *mdp_kms, uint32_t irqmask) | |
| { | |
| struct mdp_irq_wait wait = { | |
| .irq = { | |
| .irq = wait_irq, | |
| .irqmask = irqmask, | |
| }, | |
| .count = 1, | |
| }; | |
| mdp_irq_register(mdp_kms, &wait.irq); | |
| wait_event_timeout(wait_event, (wait.count <= 0), | |
| msecs_to_jiffies(100)); | |
| mdp_irq_unregister(mdp_kms, &wait.irq); | |
| } | |
| void mdp_irq_register(struct mdp_kms *mdp_kms, struct mdp_irq *irq) | |
| { | |
| unsigned long flags; | |
| bool needs_update = false; | |
| spin_lock_irqsave(&list_lock, flags); | |
| if (!irq->registered) { | |
| irq->registered = true; | |
| list_add(&irq->node, &mdp_kms->irq_list); | |
| needs_update = !mdp_kms->in_irq; | |
| } | |
| spin_unlock_irqrestore(&list_lock, flags); | |
| if (needs_update) | |
| mdp_irq_update(mdp_kms); | |
| } | |
| void mdp_irq_unregister(struct mdp_kms *mdp_kms, struct mdp_irq *irq) | |
| { | |
| unsigned long flags; | |
| bool needs_update = false; | |
| spin_lock_irqsave(&list_lock, flags); | |
| if (irq->registered) { | |
| irq->registered = false; | |
| list_del(&irq->node); | |
| needs_update = !mdp_kms->in_irq; | |
| } | |
| spin_unlock_irqrestore(&list_lock, flags); | |
| if (needs_update) | |
| mdp_irq_update(mdp_kms); | |
| } |