2005-09-26 16:04:21 +10:00
/*
* Miscellaneous procedures for dealing with the PowerMac hardware .
* Contains support for the backlight .
*
* Copyright ( C ) 2000 Benjamin Herrenschmidt
2006-06-25 05:47:08 -07:00
* Copyright ( C ) 2006 Michael Hanselmann < linux - kernel @ hansmi . ch >
2005-09-26 16:04:21 +10:00
*
*/
# include <linux/kernel.h>
2006-06-25 05:47:08 -07:00
# include <linux/fb.h>
# include <linux/backlight.h>
2006-07-30 03:04:19 -07:00
# include <linux/adb.h>
# include <linux/pmu.h>
2011-07-26 16:09:06 -07:00
# include <linux/atomic.h>
2011-05-27 10:46:24 -04:00
# include <linux/export.h>
2005-09-26 16:04:21 +10:00
# include <asm/prom.h>
# include <asm/backlight.h>
2006-06-25 05:47:08 -07:00
# define OLD_BACKLIGHT_MAX 15
2005-09-26 16:04:21 +10:00
2006-12-05 19:36:26 +00:00
static void pmac_backlight_key_worker ( struct work_struct * work ) ;
static void pmac_backlight_set_legacy_worker ( struct work_struct * work ) ;
2006-07-30 03:04:19 -07:00
2006-12-05 19:36:26 +00:00
static DECLARE_WORK ( pmac_backlight_key_work , pmac_backlight_key_worker ) ;
static DECLARE_WORK ( pmac_backlight_set_legacy_work , pmac_backlight_set_legacy_worker ) ;
2006-07-10 04:44:45 -07:00
2006-07-30 03:04:19 -07:00
/* Although these variables are used in interrupt context, it makes no sense to
* protect them . No user is able to produce enough key events per second and
2006-07-10 04:44:45 -07:00
* notice the errors that might happen .
*/
static int pmac_backlight_key_queued ;
2006-07-30 03:04:19 -07:00
static int pmac_backlight_set_legacy_queued ;
/* The via-pmu code allows the backlight to be grabbed, in which case the
* in - kernel control of the brightness needs to be disabled . This should
* only be used by really old PowerBooks .
*/
static atomic_t kernel_backlight_disabled = ATOMIC_INIT ( 0 ) ;
2006-07-10 04:44:45 -07:00
2007-02-08 22:25:09 +00:00
/* Protect the pmac_backlight variable below.
You should hold this lock when using the pmac_backlight pointer to
prevent its potential removal . */
2006-06-25 05:47:08 -07:00
DEFINE_MUTEX ( pmac_backlight_mutex ) ;
2005-09-26 16:04:21 +10:00
2006-06-25 05:47:08 -07:00
/* Main backlight storage
*
2007-02-10 23:07:48 +00:00
* Backlight drivers in this variable are required to have the " ops "
2006-06-25 05:47:08 -07:00
* attribute set and to have an update_status function .
*
* We can only store one backlight here , but since Apple laptops have only one
* internal display , it doesn ' t matter . Other backlight drivers can be used
* independently .
*
*/
struct backlight_device * pmac_backlight ;
2005-09-26 16:04:21 +10:00
2006-06-25 05:47:08 -07:00
int pmac_has_backlight_type ( const char * type )
2005-09-26 16:04:21 +10:00
{
2007-04-24 13:53:04 +10:00
struct device_node * bk_node = of_find_node_by_name ( NULL , " backlight " ) ;
2006-06-25 05:47:08 -07:00
2005-09-26 16:04:21 +10:00
if ( bk_node ) {
2007-04-03 22:26:41 +10:00
const char * prop = of_get_property ( bk_node ,
2006-07-12 15:40:29 +10:00
" backlight-control " , NULL ) ;
2007-04-24 13:53:04 +10:00
if ( prop & & strncmp ( prop , type , strlen ( type ) ) = = 0 ) {
of_node_put ( bk_node ) ;
2006-06-25 05:47:08 -07:00
return 1 ;
2007-04-24 13:53:04 +10:00
}
of_node_put ( bk_node ) ;
2005-09-26 16:04:21 +10:00
}
2006-06-25 05:47:08 -07:00
return 0 ;
2005-09-26 16:04:21 +10:00
}
2006-06-25 05:47:08 -07:00
int pmac_backlight_curve_lookup ( struct fb_info * info , int value )
2005-09-26 16:04:21 +10:00
{
2006-06-25 05:47:08 -07:00
int level = ( FB_BACKLIGHT_LEVELS - 1 ) ;
if ( info & & info - > bl_dev ) {
int i , max = 0 ;
/* Look for biggest value */
for ( i = 0 ; i < FB_BACKLIGHT_LEVELS ; i + + )
max = max ( ( int ) info - > bl_curve [ i ] , max ) ;
/* Look for nearest value */
for ( i = 0 ; i < FB_BACKLIGHT_LEVELS ; i + + ) {
int diff = abs ( info - > bl_curve [ i ] - value ) ;
if ( diff < max ) {
max = diff ;
level = i ;
}
}
}
return level ;
2005-09-26 16:04:21 +10:00
}
2006-12-05 19:36:26 +00:00
static void pmac_backlight_key_worker ( struct work_struct * work )
2005-09-26 16:04:21 +10:00
{
2006-07-30 03:04:19 -07:00
if ( atomic_read ( & kernel_backlight_disabled ) )
return ;
2006-06-25 05:47:08 -07:00
mutex_lock ( & pmac_backlight_mutex ) ;
if ( pmac_backlight ) {
struct backlight_properties * props ;
int brightness ;
2007-02-10 23:07:48 +00:00
props = & pmac_backlight - > props ;
2006-06-25 05:47:08 -07:00
brightness = props - > brightness +
2006-07-10 04:44:45 -07:00
( ( pmac_backlight_key_queued ? - 1 : 1 ) *
( props - > max_brightness / 15 ) ) ;
2006-06-25 05:47:08 -07:00
if ( brightness < 0 )
brightness = 0 ;
else if ( brightness > props - > max_brightness )
brightness = props - > max_brightness ;
props - > brightness = brightness ;
2007-02-08 22:25:09 +00:00
backlight_update_status ( pmac_backlight ) ;
2006-06-25 05:47:08 -07:00
}
mutex_unlock ( & pmac_backlight_mutex ) ;
2005-09-26 16:04:21 +10:00
}
2006-06-25 05:47:08 -07:00
2006-07-30 03:04:19 -07:00
/* This function is called in interrupt context */
2006-07-10 04:44:45 -07:00
void pmac_backlight_key ( int direction )
2005-09-26 16:04:21 +10:00
{
2006-07-30 03:04:19 -07:00
if ( atomic_read ( & kernel_backlight_disabled ) )
return ;
2006-07-10 04:44:45 -07:00
/* we can receive multiple interrupts here, but the scheduled work
* will run only once , with the last value
*/
pmac_backlight_key_queued = direction ;
schedule_work ( & pmac_backlight_key_work ) ;
2005-09-26 16:04:21 +10:00
}
2006-07-30 03:04:19 -07:00
static int __pmac_backlight_set_legacy_brightness ( int brightness )
2005-09-26 16:04:21 +10:00
{
2006-06-25 05:47:08 -07:00
int error = - ENXIO ;
mutex_lock ( & pmac_backlight_mutex ) ;
if ( pmac_backlight ) {
struct backlight_properties * props ;
2007-02-10 23:07:48 +00:00
props = & pmac_backlight - > props ;
2006-06-25 05:47:08 -07:00
props - > brightness = brightness *
2006-06-28 04:26:55 -07:00
( props - > max_brightness + 1 ) /
( OLD_BACKLIGHT_MAX + 1 ) ;
if ( props - > brightness > props - > max_brightness )
props - > brightness = props - > max_brightness ;
else if ( props - > brightness < 0 )
props - > brightness = 0 ;
2007-02-08 22:25:09 +00:00
backlight_update_status ( pmac_backlight ) ;
2006-06-25 05:47:08 -07:00
error = 0 ;
2005-09-26 16:04:21 +10:00
}
2006-06-25 05:47:08 -07:00
mutex_unlock ( & pmac_backlight_mutex ) ;
return error ;
2005-09-26 16:04:21 +10:00
}
2006-06-25 05:47:08 -07:00
2006-12-05 19:36:26 +00:00
static void pmac_backlight_set_legacy_worker ( struct work_struct * work )
2006-07-30 03:04:19 -07:00
{
if ( atomic_read ( & kernel_backlight_disabled ) )
return ;
__pmac_backlight_set_legacy_brightness ( pmac_backlight_set_legacy_queued ) ;
}
/* This function is called in interrupt context */
void pmac_backlight_set_legacy_brightness_pmu ( int brightness ) {
if ( atomic_read ( & kernel_backlight_disabled ) )
return ;
pmac_backlight_set_legacy_queued = brightness ;
schedule_work ( & pmac_backlight_set_legacy_work ) ;
}
int pmac_backlight_set_legacy_brightness ( int brightness )
{
return __pmac_backlight_set_legacy_brightness ( brightness ) ;
}
2006-06-25 05:47:08 -07:00
int pmac_backlight_get_legacy_brightness ( )
2005-09-26 16:04:21 +10:00
{
2006-06-25 05:47:08 -07:00
int result = - ENXIO ;
2005-09-26 16:04:21 +10:00
2006-06-25 05:47:08 -07:00
mutex_lock ( & pmac_backlight_mutex ) ;
if ( pmac_backlight ) {
struct backlight_properties * props ;
2005-09-26 16:04:21 +10:00
2007-02-10 23:07:48 +00:00
props = & pmac_backlight - > props ;
2006-06-28 04:26:55 -07:00
2006-06-25 05:47:08 -07:00
result = props - > brightness *
2006-06-28 04:26:55 -07:00
( OLD_BACKLIGHT_MAX + 1 ) /
( props - > max_brightness + 1 ) ;
2006-06-25 05:47:08 -07:00
}
mutex_unlock ( & pmac_backlight_mutex ) ;
2005-09-26 16:04:21 +10:00
2006-06-25 05:47:08 -07:00
return result ;
2005-09-26 16:04:21 +10:00
}
2006-07-10 04:44:45 -07:00
2006-07-30 03:04:19 -07:00
void pmac_backlight_disable ( )
{
atomic_inc ( & kernel_backlight_disabled ) ;
}
void pmac_backlight_enable ( )
{
atomic_dec ( & kernel_backlight_disabled ) ;
}
2006-07-10 04:44:45 -07:00
EXPORT_SYMBOL_GPL ( pmac_backlight ) ;
EXPORT_SYMBOL_GPL ( pmac_backlight_mutex ) ;
EXPORT_SYMBOL_GPL ( pmac_has_backlight_type ) ;