2011-11-03 22:22:04 +04:00
/**************************************************************************
* Copyright ( c ) 2007 , Intel Corporation .
* All Rights Reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Intel funded Tungsten Graphics ( http : //www.tungstengraphics.com) to
* develop this driver .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
*/
# include <drm/drmP.h>
# include "psb_drv.h"
# include "psb_reg.h"
# include "psb_intel_reg.h"
# include "power.h"
2012-03-08 20:02:20 +04:00
# include "psb_irq.h"
# include "mdfld_output.h"
2011-11-03 22:22:04 +04:00
/*
* inline functions
*/
static inline u32
psb_pipestat ( int pipe )
{
if ( pipe = = 0 )
return PIPEASTAT ;
if ( pipe = = 1 )
return PIPEBSTAT ;
if ( pipe = = 2 )
return PIPECSTAT ;
BUG ( ) ;
}
static inline u32
mid_pipe_event ( int pipe )
{
if ( pipe = = 0 )
return _PSB_PIPEA_EVENT_FLAG ;
if ( pipe = = 1 )
return _MDFLD_PIPEB_EVENT_FLAG ;
if ( pipe = = 2 )
return _MDFLD_PIPEC_EVENT_FLAG ;
BUG ( ) ;
}
static inline u32
mid_pipe_vsync ( int pipe )
{
if ( pipe = = 0 )
return _PSB_VSYNC_PIPEA_FLAG ;
if ( pipe = = 1 )
return _PSB_VSYNC_PIPEB_FLAG ;
if ( pipe = = 2 )
return _MDFLD_PIPEC_VBLANK_FLAG ;
BUG ( ) ;
}
static inline u32
mid_pipeconf ( int pipe )
{
if ( pipe = = 0 )
return PIPEACONF ;
if ( pipe = = 1 )
return PIPEBCONF ;
if ( pipe = = 2 )
return PIPECCONF ;
BUG ( ) ;
}
void
psb_enable_pipestat ( struct drm_psb_private * dev_priv , int pipe , u32 mask )
{
if ( ( dev_priv - > pipestat [ pipe ] & mask ) ! = mask ) {
u32 reg = psb_pipestat ( pipe ) ;
dev_priv - > pipestat [ pipe ] | = mask ;
/* Enable the interrupt, clear any pending status */
if ( gma_power_begin ( dev_priv - > dev , false ) ) {
u32 writeVal = PSB_RVDC32 ( reg ) ;
writeVal | = ( mask | ( mask > > 16 ) ) ;
PSB_WVDC32 ( writeVal , reg ) ;
( void ) PSB_RVDC32 ( reg ) ;
gma_power_end ( dev_priv - > dev ) ;
}
}
}
void
psb_disable_pipestat ( struct drm_psb_private * dev_priv , int pipe , u32 mask )
{
if ( ( dev_priv - > pipestat [ pipe ] & mask ) ! = 0 ) {
u32 reg = psb_pipestat ( pipe ) ;
dev_priv - > pipestat [ pipe ] & = ~ mask ;
if ( gma_power_begin ( dev_priv - > dev , false ) ) {
u32 writeVal = PSB_RVDC32 ( reg ) ;
writeVal & = ~ mask ;
PSB_WVDC32 ( writeVal , reg ) ;
( void ) PSB_RVDC32 ( reg ) ;
gma_power_end ( dev_priv - > dev ) ;
}
}
}
2012-03-08 20:15:47 +04:00
static void mid_enable_pipe_event ( struct drm_psb_private * dev_priv , int pipe )
2011-11-03 22:22:04 +04:00
{
if ( gma_power_begin ( dev_priv - > dev , false ) ) {
u32 pipe_event = mid_pipe_event ( pipe ) ;
dev_priv - > vdc_irq_mask | = pipe_event ;
PSB_WVDC32 ( ~ dev_priv - > vdc_irq_mask , PSB_INT_MASK_R ) ;
PSB_WVDC32 ( dev_priv - > vdc_irq_mask , PSB_INT_ENABLE_R ) ;
gma_power_end ( dev_priv - > dev ) ;
}
}
2012-03-08 20:15:47 +04:00
static void mid_disable_pipe_event ( struct drm_psb_private * dev_priv , int pipe )
2011-11-03 22:22:04 +04:00
{
if ( dev_priv - > pipestat [ pipe ] = = 0 ) {
if ( gma_power_begin ( dev_priv - > dev , false ) ) {
u32 pipe_event = mid_pipe_event ( pipe ) ;
dev_priv - > vdc_irq_mask & = ~ pipe_event ;
PSB_WVDC32 ( ~ dev_priv - > vdc_irq_mask , PSB_INT_MASK_R ) ;
PSB_WVDC32 ( dev_priv - > vdc_irq_mask , PSB_INT_ENABLE_R ) ;
gma_power_end ( dev_priv - > dev ) ;
}
}
}
/**
* Display controller interrupt handler for pipe event .
*
*/
2011-11-30 02:20:34 +04:00
static void mid_pipe_event_handler ( struct drm_device * dev , int pipe )
2011-11-03 22:22:04 +04:00
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
uint32_t pipe_stat_val = 0 ;
uint32_t pipe_stat_reg = psb_pipestat ( pipe ) ;
uint32_t pipe_enable = dev_priv - > pipestat [ pipe ] ;
uint32_t pipe_status = dev_priv - > pipestat [ pipe ] > > 16 ;
2011-11-30 02:20:34 +04:00
uint32_t pipe_clear ;
2011-11-03 22:22:04 +04:00
uint32_t i = 0 ;
spin_lock ( & dev_priv - > irqmask_lock ) ;
pipe_stat_val = PSB_RVDC32 ( pipe_stat_reg ) ;
pipe_stat_val & = pipe_enable | pipe_status ;
pipe_stat_val & = pipe_stat_val > > 16 ;
spin_unlock ( & dev_priv - > irqmask_lock ) ;
2011-11-30 02:20:34 +04:00
/* Clear the 2nd level interrupt status bits
* Sometimes the bits are very sticky so we repeat until they unstick */
for ( i = 0 ; i < 0xffff ; i + + ) {
2011-11-03 22:22:04 +04:00
PSB_WVDC32 ( PSB_RVDC32 ( pipe_stat_reg ) , pipe_stat_reg ) ;
2011-11-30 02:20:34 +04:00
pipe_clear = PSB_RVDC32 ( pipe_stat_reg ) & pipe_status ;
2011-11-03 22:22:04 +04:00
2011-11-30 02:20:34 +04:00
if ( pipe_clear = = 0 )
2011-11-03 22:22:04 +04:00
break ;
}
2011-11-30 02:20:34 +04:00
if ( pipe_clear )
2011-11-03 22:22:04 +04:00
dev_err ( dev - > dev ,
2011-11-30 02:20:34 +04:00
" %s, can't clear status bits for pipe %d, its value = 0x%x. \n " ,
__func__ , pipe , PSB_RVDC32 ( pipe_stat_reg ) ) ;
2011-11-03 22:22:04 +04:00
if ( pipe_stat_val & PIPE_VBLANK_STATUS )
2011-11-30 02:20:34 +04:00
drm_handle_vblank ( dev , pipe ) ;
2011-11-03 22:22:04 +04:00
if ( pipe_stat_val & PIPE_TE_STATUS )
drm_handle_vblank ( dev , pipe ) ;
}
/*
* Display controller interrupt handler .
*/
static void psb_vdc_interrupt ( struct drm_device * dev , uint32_t vdc_stat )
{
2012-05-03 18:06:18 +04:00
if ( vdc_stat & _PSB_IRQ_ASLE )
psb_intel_opregion_asle_intr ( dev ) ;
2011-11-30 02:20:34 +04:00
if ( vdc_stat & _PSB_VSYNC_PIPEA_FLAG )
2011-11-03 22:22:04 +04:00
mid_pipe_event_handler ( dev , 0 ) ;
2011-11-30 02:20:34 +04:00
if ( vdc_stat & _PSB_VSYNC_PIPEB_FLAG )
mid_pipe_event_handler ( dev , 1 ) ;
2011-11-03 22:22:04 +04:00
}
2014-01-03 04:52:46 +04:00
/*
* SGX interrupt handler
*/
static void psb_sgx_interrupt ( struct drm_device * dev , u32 stat_1 , u32 stat_2 )
{
struct drm_psb_private * dev_priv = dev - > dev_private ;
u32 val , addr ;
int error = false ;
if ( stat_1 & _PSB_CE_TWOD_COMPLETE )
val = PSB_RSGX32 ( PSB_CR_2D_BLIT_STATUS ) ;
if ( stat_2 & _PSB_CE2_BIF_REQUESTER_FAULT ) {
val = PSB_RSGX32 ( PSB_CR_BIF_INT_STAT ) ;
addr = PSB_RSGX32 ( PSB_CR_BIF_FAULT ) ;
if ( val ) {
if ( val & _PSB_CBI_STAT_PF_N_RW )
DRM_ERROR ( " SGX MMU page fault: " ) ;
else
DRM_ERROR ( " SGX MMU read / write protection fault: " ) ;
if ( val & _PSB_CBI_STAT_FAULT_CACHE )
DRM_ERROR ( " \t Cache requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_TA )
DRM_ERROR ( " \t TA requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_VDM )
DRM_ERROR ( " \t VDM requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_2D )
DRM_ERROR ( " \t 2D requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_PBE )
DRM_ERROR ( " \t PBE requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_TSP )
DRM_ERROR ( " \t TSP requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_ISP )
DRM_ERROR ( " \t ISP requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_USSEPDS )
DRM_ERROR ( " \t USSEPDS requestor " ) ;
if ( val & _PSB_CBI_STAT_FAULT_HOST )
DRM_ERROR ( " \t Host requestor " ) ;
DRM_ERROR ( " \t MMU failing address is 0x%08x. \n " ,
( unsigned int ) addr ) ;
error = true ;
}
}
/* Clear bits */
PSB_WSGX32 ( stat_1 , PSB_CR_EVENT_HOST_CLEAR ) ;
PSB_WSGX32 ( stat_2 , PSB_CR_EVENT_HOST_CLEAR2 ) ;
PSB_RSGX32 ( PSB_CR_EVENT_HOST_CLEAR2 ) ;
}
2013-12-11 14:34:42 +04:00
irqreturn_t psb_irq_handler ( int irq , void * arg )
2011-11-03 22:22:04 +04:00
{
2012-04-25 17:38:20 +04:00
struct drm_device * dev = arg ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
uint32_t vdc_stat , dsp_int = 0 , sgx_int = 0 , hotplug_int = 0 ;
2014-01-03 04:52:46 +04:00
u32 sgx_stat_1 , sgx_stat_2 ;
2011-11-03 22:22:04 +04:00
int handled = 0 ;
spin_lock ( & dev_priv - > irqmask_lock ) ;
vdc_stat = PSB_RVDC32 ( PSB_INT_IDENTITY_R ) ;
2013-04-24 19:36:01 +04:00
if ( vdc_stat & ( _PSB_PIPE_EVENT_FLAG | _PSB_IRQ_ASLE ) )
2011-11-30 02:20:34 +04:00
dsp_int = 1 ;
/* FIXME: Handle Medfield
2011-11-03 22:22:04 +04:00
if ( vdc_stat & _MDFLD_DISP_ALL_IRQ_FLAG )
dsp_int = 1 ;
2011-11-30 02:20:34 +04:00
*/
2011-11-03 22:22:04 +04:00
if ( vdc_stat & _PSB_IRQ_SGX_FLAG )
sgx_int = 1 ;
2012-04-25 17:38:20 +04:00
if ( vdc_stat & _PSB_IRQ_DISP_HOTSYNC )
hotplug_int = 1 ;
2011-11-03 22:22:04 +04:00
vdc_stat & = dev_priv - > vdc_irq_mask ;
spin_unlock ( & dev_priv - > irqmask_lock ) ;
if ( dsp_int & & gma_power_is_on ( dev ) ) {
psb_vdc_interrupt ( dev , vdc_stat ) ;
handled = 1 ;
}
if ( sgx_int ) {
2014-01-03 04:52:46 +04:00
sgx_stat_1 = PSB_RSGX32 ( PSB_CR_EVENT_STATUS ) ;
sgx_stat_2 = PSB_RSGX32 ( PSB_CR_EVENT_STATUS2 ) ;
psb_sgx_interrupt ( dev , sgx_stat_1 , sgx_stat_2 ) ;
2011-11-03 22:22:04 +04:00
handled = 1 ;
}
2012-04-25 17:38:20 +04:00
/* Note: this bit has other meanings on some devices, so we will
need to address that later if it ever matters */
if ( hotplug_int & & dev_priv - > ops - > hotplug ) {
handled = dev_priv - > ops - > hotplug ( dev ) ;
REG_WRITE ( PORT_HOTPLUG_STAT , REG_READ ( PORT_HOTPLUG_STAT ) ) ;
}
2011-11-03 22:22:04 +04:00
PSB_WVDC32 ( vdc_stat , PSB_INT_IDENTITY_R ) ;
( void ) PSB_RVDC32 ( PSB_INT_IDENTITY_R ) ;
2013-12-11 14:34:45 +04:00
rmb ( ) ;
2011-11-03 22:22:04 +04:00
if ( ! handled )
return IRQ_NONE ;
return IRQ_HANDLED ;
}
void psb_irq_preinstall ( struct drm_device * dev )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
unsigned long irqflags ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
2014-01-03 04:52:46 +04:00
if ( gma_power_is_on ( dev ) ) {
2011-11-03 22:22:04 +04:00
PSB_WVDC32 ( 0xFFFFFFFF , PSB_HWSTAM ) ;
2014-01-03 04:52:46 +04:00
PSB_WVDC32 ( 0x00000000 , PSB_INT_MASK_R ) ;
PSB_WVDC32 ( 0x00000000 , PSB_INT_ENABLE_R ) ;
PSB_WSGX32 ( 0x00000000 , PSB_CR_EVENT_HOST_ENABLE ) ;
PSB_RSGX32 ( PSB_CR_EVENT_HOST_ENABLE ) ;
}
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 0 ] . enabled )
2011-11-30 02:20:34 +04:00
dev_priv - > vdc_irq_mask | = _PSB_VSYNC_PIPEA_FLAG ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 1 ] . enabled )
2011-11-30 02:20:34 +04:00
dev_priv - > vdc_irq_mask | = _PSB_VSYNC_PIPEB_FLAG ;
/* FIXME: Handle Medfield irq mask
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 1 ] . enabled )
2011-11-03 22:22:04 +04:00
dev_priv - > vdc_irq_mask | = _MDFLD_PIPEB_EVENT_FLAG ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 2 ] . enabled )
2011-11-03 22:22:04 +04:00
dev_priv - > vdc_irq_mask | = _MDFLD_PIPEC_EVENT_FLAG ;
2011-11-30 02:20:34 +04:00
*/
2011-11-03 22:22:04 +04:00
2012-04-25 17:38:20 +04:00
/* Revisit this area - want per device masks ? */
if ( dev_priv - > ops - > hotplug )
dev_priv - > vdc_irq_mask | = _PSB_IRQ_DISP_HOTSYNC ;
2014-01-03 04:52:46 +04:00
dev_priv - > vdc_irq_mask | = _PSB_IRQ_ASLE | _PSB_IRQ_SGX_FLAG ;
2012-04-25 17:38:20 +04:00
2011-11-30 02:20:34 +04:00
/* This register is safe even if display island is off */
2011-11-03 22:22:04 +04:00
PSB_WVDC32 ( ~ dev_priv - > vdc_irq_mask , PSB_INT_MASK_R ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
}
int psb_irq_postinstall ( struct drm_device * dev )
{
2014-01-03 04:52:46 +04:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
2011-11-03 22:22:04 +04:00
unsigned long irqflags ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
2014-01-03 04:52:46 +04:00
/* Enable 2D and MMU fault interrupts */
PSB_WSGX32 ( _PSB_CE2_BIF_REQUESTER_FAULT , PSB_CR_EVENT_HOST_ENABLE2 ) ;
PSB_WSGX32 ( _PSB_CE_TWOD_COMPLETE , PSB_CR_EVENT_HOST_ENABLE ) ;
PSB_RSGX32 ( PSB_CR_EVENT_HOST_ENABLE ) ; /* Post */
2011-11-03 22:22:04 +04:00
/* This register is safe even if display island is off */
PSB_WVDC32 ( dev_priv - > vdc_irq_mask , PSB_INT_ENABLE_R ) ;
PSB_WVDC32 ( 0xFFFFFFFF , PSB_HWSTAM ) ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 0 ] . enabled )
2011-11-03 22:22:04 +04:00
psb_enable_pipestat ( dev_priv , 0 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
else
psb_disable_pipestat ( dev_priv , 0 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 1 ] . enabled )
2011-11-03 22:22:04 +04:00
psb_enable_pipestat ( dev_priv , 1 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
else
psb_disable_pipestat ( dev_priv , 1 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 2 ] . enabled )
2011-11-03 22:22:04 +04:00
psb_enable_pipestat ( dev_priv , 2 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
else
psb_disable_pipestat ( dev_priv , 2 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
2012-04-25 17:38:20 +04:00
if ( dev_priv - > ops - > hotplug_enable )
dev_priv - > ops - > hotplug_enable ( dev , true ) ;
2011-11-03 22:22:04 +04:00
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
return 0 ;
}
void psb_irq_uninstall ( struct drm_device * dev )
{
2012-04-25 17:38:20 +04:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
2011-11-03 22:22:04 +04:00
unsigned long irqflags ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
2012-04-25 17:38:20 +04:00
if ( dev_priv - > ops - > hotplug_enable )
dev_priv - > ops - > hotplug_enable ( dev , false ) ;
2011-11-03 22:22:04 +04:00
PSB_WVDC32 ( 0xFFFFFFFF , PSB_HWSTAM ) ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 0 ] . enabled )
2011-11-03 22:22:04 +04:00
psb_disable_pipestat ( dev_priv , 0 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 1 ] . enabled )
2011-11-03 22:22:04 +04:00
psb_disable_pipestat ( dev_priv , 1 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
2013-10-04 15:53:36 +04:00
if ( dev - > vblank [ 2 ] . enabled )
2011-11-03 22:22:04 +04:00
psb_disable_pipestat ( dev_priv , 2 , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
dev_priv - > vdc_irq_mask & = _PSB_IRQ_SGX_FLAG |
_PSB_IRQ_MSVDX_FLAG |
_LNC_IRQ_TOPAZ_FLAG ;
/* These two registers are safe even if display island is off */
PSB_WVDC32 ( ~ dev_priv - > vdc_irq_mask , PSB_INT_MASK_R ) ;
PSB_WVDC32 ( dev_priv - > vdc_irq_mask , PSB_INT_ENABLE_R ) ;
wmb ( ) ;
/* This register is safe even if display island is off */
PSB_WVDC32 ( PSB_RVDC32 ( PSB_INT_IDENTITY_R ) , PSB_INT_IDENTITY_R ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
}
void psb_irq_turn_on_dpst ( struct drm_device * dev )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
u32 hist_reg ;
u32 pwm_reg ;
if ( gma_power_begin ( dev , false ) ) {
PSB_WVDC32 ( 1 < < 31 , HISTOGRAM_LOGIC_CONTROL ) ;
hist_reg = PSB_RVDC32 ( HISTOGRAM_LOGIC_CONTROL ) ;
PSB_WVDC32 ( 1 < < 31 , HISTOGRAM_INT_CONTROL ) ;
hist_reg = PSB_RVDC32 ( HISTOGRAM_INT_CONTROL ) ;
PSB_WVDC32 ( 0x80010100 , PWM_CONTROL_LOGIC ) ;
pwm_reg = PSB_RVDC32 ( PWM_CONTROL_LOGIC ) ;
PSB_WVDC32 ( pwm_reg | PWM_PHASEIN_ENABLE
| PWM_PHASEIN_INT_ENABLE ,
PWM_CONTROL_LOGIC ) ;
pwm_reg = PSB_RVDC32 ( PWM_CONTROL_LOGIC ) ;
psb_enable_pipestat ( dev_priv , 0 , PIPE_DPST_EVENT_ENABLE ) ;
hist_reg = PSB_RVDC32 ( HISTOGRAM_INT_CONTROL ) ;
PSB_WVDC32 ( hist_reg | HISTOGRAM_INT_CTRL_CLEAR ,
HISTOGRAM_INT_CONTROL ) ;
pwm_reg = PSB_RVDC32 ( PWM_CONTROL_LOGIC ) ;
PSB_WVDC32 ( pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE ,
PWM_CONTROL_LOGIC ) ;
gma_power_end ( dev ) ;
}
}
int psb_irq_enable_dpst ( struct drm_device * dev )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
unsigned long irqflags ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
/* enable DPST */
mid_enable_pipe_event ( dev_priv , 0 ) ;
psb_irq_turn_on_dpst ( dev ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
return 0 ;
}
void psb_irq_turn_off_dpst ( struct drm_device * dev )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
u32 hist_reg ;
u32 pwm_reg ;
if ( gma_power_begin ( dev , false ) ) {
PSB_WVDC32 ( 0x00000000 , HISTOGRAM_INT_CONTROL ) ;
hist_reg = PSB_RVDC32 ( HISTOGRAM_INT_CONTROL ) ;
psb_disable_pipestat ( dev_priv , 0 , PIPE_DPST_EVENT_ENABLE ) ;
pwm_reg = PSB_RVDC32 ( PWM_CONTROL_LOGIC ) ;
2012-05-03 18:08:50 +04:00
PSB_WVDC32 ( pwm_reg & ~ PWM_PHASEIN_INT_ENABLE ,
2011-11-03 22:22:04 +04:00
PWM_CONTROL_LOGIC ) ;
pwm_reg = PSB_RVDC32 ( PWM_CONTROL_LOGIC ) ;
gma_power_end ( dev ) ;
}
}
int psb_irq_disable_dpst ( struct drm_device * dev )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
unsigned long irqflags ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
mid_disable_pipe_event ( dev_priv , 0 ) ;
psb_irq_turn_off_dpst ( dev ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
return 0 ;
}
/*
* It is used to enable VBLANK interrupt
*/
2015-09-24 19:35:31 +03:00
int psb_enable_vblank ( struct drm_device * dev , unsigned int pipe )
2011-11-03 22:22:04 +04:00
{
struct drm_psb_private * dev_priv = dev - > dev_private ;
unsigned long irqflags ;
uint32_t reg_val = 0 ;
uint32_t pipeconf_reg = mid_pipeconf ( pipe ) ;
2012-03-08 20:02:20 +04:00
/* Medfield is different - we should perhaps extract out vblank
and blacklight etc ops */
if ( IS_MFLD ( dev ) )
return mdfld_enable_te ( dev , pipe ) ;
2011-11-03 22:22:04 +04:00
if ( gma_power_begin ( dev , false ) ) {
reg_val = REG_READ ( pipeconf_reg ) ;
gma_power_end ( dev ) ;
}
if ( ! ( reg_val & PIPEACONF_ENABLE ) )
return - EINVAL ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
2011-11-30 02:20:34 +04:00
if ( pipe = = 0 )
dev_priv - > vdc_irq_mask | = _PSB_VSYNC_PIPEA_FLAG ;
else if ( pipe = = 1 )
dev_priv - > vdc_irq_mask | = _PSB_VSYNC_PIPEB_FLAG ;
PSB_WVDC32 ( ~ dev_priv - > vdc_irq_mask , PSB_INT_MASK_R ) ;
PSB_WVDC32 ( dev_priv - > vdc_irq_mask , PSB_INT_ENABLE_R ) ;
2011-11-03 22:22:04 +04:00
psb_enable_pipestat ( dev_priv , pipe , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
return 0 ;
}
/*
* It is used to disable VBLANK interrupt
*/
2015-09-24 19:35:31 +03:00
void psb_disable_vblank ( struct drm_device * dev , unsigned int pipe )
2011-11-03 22:22:04 +04:00
{
struct drm_psb_private * dev_priv = dev - > dev_private ;
unsigned long irqflags ;
2012-03-08 20:02:20 +04:00
if ( IS_MFLD ( dev ) )
mdfld_disable_te ( dev , pipe ) ;
2011-11-03 22:22:04 +04:00
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
2011-11-30 02:20:34 +04:00
if ( pipe = = 0 )
dev_priv - > vdc_irq_mask & = ~ _PSB_VSYNC_PIPEA_FLAG ;
else if ( pipe = = 1 )
dev_priv - > vdc_irq_mask & = ~ _PSB_VSYNC_PIPEB_FLAG ;
PSB_WVDC32 ( ~ dev_priv - > vdc_irq_mask , PSB_INT_MASK_R ) ;
PSB_WVDC32 ( dev_priv - > vdc_irq_mask , PSB_INT_ENABLE_R ) ;
2011-11-03 22:22:04 +04:00
psb_disable_pipestat ( dev_priv , pipe , PIPE_VBLANK_INTERRUPT_ENABLE ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
}
2012-03-08 20:02:20 +04:00
/*
* It is used to enable TE interrupt
*/
int mdfld_enable_te ( struct drm_device * dev , int pipe )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
unsigned long irqflags ;
uint32_t reg_val = 0 ;
uint32_t pipeconf_reg = mid_pipeconf ( pipe ) ;
if ( gma_power_begin ( dev , false ) ) {
reg_val = REG_READ ( pipeconf_reg ) ;
gma_power_end ( dev ) ;
}
if ( ! ( reg_val & PIPEACONF_ENABLE ) )
return - EINVAL ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
mid_enable_pipe_event ( dev_priv , pipe ) ;
psb_enable_pipestat ( dev_priv , pipe , PIPE_TE_ENABLE ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
return 0 ;
}
/*
* It is used to disable TE interrupt
*/
void mdfld_disable_te ( struct drm_device * dev , int pipe )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
unsigned long irqflags ;
if ( ! dev_priv - > dsr_enable )
return ;
spin_lock_irqsave ( & dev_priv - > irqmask_lock , irqflags ) ;
mid_disable_pipe_event ( dev_priv , pipe ) ;
psb_disable_pipestat ( dev_priv , pipe , PIPE_TE_ENABLE ) ;
spin_unlock_irqrestore ( & dev_priv - > irqmask_lock , irqflags ) ;
}
2011-11-03 22:22:04 +04:00
/* Called from drm generic code, passed a 'crtc', which
* we use as a pipe index
*/
2015-09-24 19:35:31 +03:00
u32 psb_get_vblank_counter ( struct drm_device * dev , unsigned int pipe )
2011-11-03 22:22:04 +04:00
{
uint32_t high_frame = PIPEAFRAMEHIGH ;
uint32_t low_frame = PIPEAFRAMEPIXEL ;
uint32_t pipeconf_reg = PIPEACONF ;
uint32_t reg_val = 0 ;
uint32_t high1 = 0 , high2 = 0 , low = 0 , count = 0 ;
switch ( pipe ) {
case 0 :
break ;
case 1 :
high_frame = PIPEBFRAMEHIGH ;
low_frame = PIPEBFRAMEPIXEL ;
pipeconf_reg = PIPEBCONF ;
break ;
case 2 :
high_frame = PIPECFRAMEHIGH ;
low_frame = PIPECFRAMEPIXEL ;
pipeconf_reg = PIPECCONF ;
break ;
default :
dev_err ( dev - > dev , " %s, invalid pipe. \n " , __func__ ) ;
return 0 ;
}
if ( ! gma_power_begin ( dev , false ) )
return 0 ;
reg_val = REG_READ ( pipeconf_reg ) ;
if ( ! ( reg_val & PIPEACONF_ENABLE ) ) {
2015-09-24 19:35:31 +03:00
dev_err ( dev - > dev , " trying to get vblank count for disabled pipe %u \n " ,
2011-11-03 22:22:04 +04:00
pipe ) ;
goto psb_get_vblank_counter_exit ;
}
/*
* High & low register fields aren ' t synchronized , so make sure
* we get a low value that ' s stable across two reads of the high
* register .
*/
do {
high1 = ( ( REG_READ ( high_frame ) & PIPE_FRAME_HIGH_MASK ) > >
PIPE_FRAME_HIGH_SHIFT ) ;
low = ( ( REG_READ ( low_frame ) & PIPE_FRAME_LOW_MASK ) > >
PIPE_FRAME_LOW_SHIFT ) ;
high2 = ( ( REG_READ ( high_frame ) & PIPE_FRAME_HIGH_MASK ) > >
PIPE_FRAME_HIGH_SHIFT ) ;
} while ( high1 ! = high2 ) ;
count = ( high1 < < 8 ) | low ;
psb_get_vblank_counter_exit :
gma_power_end ( dev ) ;
return count ;
}