2006-06-25 16:47:08 +04:00
/*
* Backlight code for via - pmu
*
* Copyright ( C ) 1998 Paul Mackerras and Fabio Riccardi .
* Copyright ( C ) 2001 - 2002 Benjamin Herrenschmidt
* Copyright ( C ) 2006 Michael Hanselmann < linux - kernel @ hansmi . ch >
*
*/
# include <asm/ptrace.h>
# include <linux/adb.h>
# include <linux/pmu.h>
# include <asm/backlight.h>
# include <asm/prom.h>
# define MAX_PMU_LEVEL 0xFF
2007-02-11 02:07:48 +03:00
static struct backlight_ops pmu_backlight_data ;
2006-09-29 12:58:37 +04:00
static DEFINE_SPINLOCK ( pmu_backlight_lock ) ;
2006-07-30 14:04:19 +04:00
static int sleeping ;
2006-09-01 08:27:49 +04:00
static u8 bl_curve [ FB_BACKLIGHT_LEVELS ] ;
2006-06-25 16:47:08 +04:00
2006-09-01 08:27:49 +04:00
static void pmu_backlight_init_curve ( u8 off , u8 min , u8 max )
{
unsigned int i , flat , count , range = ( max - min ) ;
bl_curve [ 0 ] = off ;
for ( flat = 1 ; flat < ( FB_BACKLIGHT_LEVELS / 16 ) ; + + flat )
bl_curve [ flat ] = min ;
count = FB_BACKLIGHT_LEVELS * 15 / 16 ;
for ( i = 0 ; i < count ; + + i )
bl_curve [ flat + i ] = min + ( range * ( i + 1 ) / count ) ;
}
static int pmu_backlight_curve_lookup ( int value )
{
int level = ( FB_BACKLIGHT_LEVELS - 1 ) ;
int i , max = 0 ;
/* Look for biggest value */
for ( i = 0 ; i < FB_BACKLIGHT_LEVELS ; i + + )
max = max ( ( int ) bl_curve [ i ] , max ) ;
/* Look for nearest value */
for ( i = 0 ; i < FB_BACKLIGHT_LEVELS ; i + + ) {
int diff = abs ( bl_curve [ i ] - value ) ;
if ( diff < max ) {
max = diff ;
level = i ;
}
}
return level ;
}
static int pmu_backlight_get_level_brightness ( int level )
2006-06-25 16:47:08 +04:00
{
int pmulevel ;
/* Get and convert the value */
2006-09-01 08:27:49 +04:00
pmulevel = bl_curve [ level ] * FB_BACKLIGHT_MAX / MAX_PMU_LEVEL ;
2006-06-25 16:47:08 +04:00
if ( pmulevel < 0 )
pmulevel = 0 ;
else if ( pmulevel > MAX_PMU_LEVEL )
pmulevel = MAX_PMU_LEVEL ;
return pmulevel ;
}
static int pmu_backlight_update_status ( struct backlight_device * bd )
{
struct adb_request req ;
2006-07-30 14:04:19 +04:00
unsigned long flags ;
2007-02-11 02:07:48 +03:00
int level = bd - > props . brightness ;
2006-06-25 16:47:08 +04:00
2006-07-30 14:04:19 +04:00
spin_lock_irqsave ( & pmu_backlight_lock , flags ) ;
/* Don't update brightness when sleeping */
if ( sleeping )
goto out ;
2006-06-25 16:47:08 +04:00
2007-02-11 02:07:48 +03:00
if ( bd - > props . power ! = FB_BLANK_UNBLANK | |
bd - > props . fb_blank ! = FB_BLANK_UNBLANK )
2006-06-25 16:47:08 +04:00
level = 0 ;
2006-07-30 14:04:19 +04:00
if ( level > 0 ) {
2006-09-01 08:27:49 +04:00
int pmulevel = pmu_backlight_get_level_brightness ( level ) ;
2006-06-25 16:47:08 +04:00
2006-07-30 14:04:19 +04:00
pmu_request ( & req , NULL , 2 , PMU_BACKLIGHT_BRIGHT , pmulevel ) ;
pmu_wait_complete ( & req ) ;
2006-06-25 16:47:08 +04:00
2006-07-30 14:04:19 +04:00
pmu_request ( & req , NULL , 2 , PMU_POWER_CTRL ,
PMU_POW_BACKLIGHT | PMU_POW_ON ) ;
pmu_wait_complete ( & req ) ;
} else {
pmu_request ( & req , NULL , 2 , PMU_POWER_CTRL ,
PMU_POW_BACKLIGHT | PMU_POW_OFF ) ;
pmu_wait_complete ( & req ) ;
}
out :
spin_unlock_irqrestore ( & pmu_backlight_lock , flags ) ;
2006-06-25 16:47:08 +04:00
return 0 ;
}
static int pmu_backlight_get_brightness ( struct backlight_device * bd )
{
2007-02-11 02:07:48 +03:00
return bd - > props . brightness ;
2006-06-25 16:47:08 +04:00
}
2007-02-11 02:07:48 +03:00
static struct backlight_ops pmu_backlight_data = {
2006-06-25 16:47:08 +04:00
. get_brightness = pmu_backlight_get_brightness ,
. update_status = pmu_backlight_update_status ,
2007-02-11 02:07:48 +03:00
2006-06-25 16:47:08 +04:00
} ;
2006-07-30 14:04:19 +04:00
# ifdef CONFIG_PM
2006-09-01 08:27:49 +04:00
void pmu_backlight_set_sleep ( int sleep )
2006-07-30 14:04:19 +04:00
{
unsigned long flags ;
spin_lock_irqsave ( & pmu_backlight_lock , flags ) ;
2006-09-01 08:27:49 +04:00
sleeping = sleep ;
2006-07-30 14:04:19 +04:00
spin_unlock_irqrestore ( & pmu_backlight_lock , flags ) ;
}
2006-09-01 08:27:49 +04:00
# endif /* CONFIG_PM */
2006-07-30 14:04:19 +04:00
void __init pmu_backlight_init ( )
2006-06-25 16:47:08 +04:00
{
struct backlight_device * bd ;
char name [ 10 ] ;
int level , autosave ;
/* Special case for the old PowerBook since I can't test on it */
autosave =
machine_is_compatible ( " AAPL,3400/2400 " ) | |
machine_is_compatible ( " AAPL,3500 " ) ;
if ( ! autosave & &
! pmac_has_backlight_type ( " pmu " ) & &
! machine_is_compatible ( " AAPL,PowerBook1998 " ) & &
! machine_is_compatible ( " PowerBook1,1 " ) )
return ;
2006-09-01 08:27:49 +04:00
snprintf ( name , sizeof ( name ) , " pmubl " ) ;
2006-06-25 16:47:08 +04:00
2006-12-30 03:48:16 +03:00
bd = backlight_device_register ( name , NULL , NULL , & pmu_backlight_data ) ;
2006-06-25 16:47:08 +04:00
if ( IS_ERR ( bd ) ) {
printk ( " pmubl: Backlight registration failed \n " ) ;
goto error ;
}
2007-02-11 02:07:48 +03:00
bd - > props . max_brightness = FB_BACKLIGHT_LEVELS - 1 ;
2006-09-01 08:27:49 +04:00
pmu_backlight_init_curve ( 0x7F , 0x46 , 0x0E ) ;
2006-06-25 16:47:08 +04:00
2007-02-11 02:07:48 +03:00
level = bd - > props . max_brightness ;
2006-06-25 16:47:08 +04:00
if ( autosave ) {
/* read autosaved value if available */
struct adb_request req ;
pmu_request ( & req , NULL , 2 , 0xd9 , 0 ) ;
pmu_wait_complete ( & req ) ;
2006-09-01 08:27:49 +04:00
level = pmu_backlight_curve_lookup (
2006-06-25 16:47:08 +04:00
( req . reply [ 0 ] > > 4 ) *
2007-02-11 02:07:48 +03:00
bd - > props . max_brightness / 15 ) ;
2006-06-25 16:47:08 +04:00
}
2007-02-11 02:07:48 +03:00
bd - > props . brightness = level ;
bd - > props . power = FB_BLANK_UNBLANK ;
2007-02-09 01:25:09 +03:00
backlight_update_status ( bd ) ;
2006-06-25 16:47:08 +04:00
printk ( " pmubl: Backlight initialized (%s) \n " , name ) ;
return ;
error :
return ;
}