2013-11-30 17:24:22 -05:00
/*
* 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 ;
BUG_ON ( ! spin_is_locked ( & list_lock ) ) ;
list_for_each_entry ( irq , & mdp_kms - > irq_list , node )
irqmask | = irq - > irqmask ;
mdp_kms - > funcs - > set_irqmask ( mdp_kms , irqmask ) ;
}
2014-12-02 11:50:06 -05:00
/* 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 )
2013-11-30 17:24:22 -05:00
{
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 ) ;
2014-03-24 09:12:24 -04:00
wait_event_timeout ( wait_event , ( wait . count < = 0 ) ,
msecs_to_jiffies ( 100 ) ) ;
2013-11-30 17:24:22 -05:00
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 )
2014-12-02 11:50:06 -05:00
mdp_irq_update ( mdp_kms ) ;
2013-11-30 17:24:22 -05:00
}
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 )
2014-12-02 11:50:06 -05:00
mdp_irq_update ( mdp_kms ) ;
2013-11-30 17:24:22 -05:00
}