2005-04-17 02:20:36 +04:00
/*
* video . c - ACPI Video Driver ( $ Revision : $ )
*
* Copyright ( C ) 2004 Luming Yu < luming . yu @ intel . com >
* Copyright ( C ) 2004 Bruno Ducrot < ducrot @ poupinou . org >
2006-12-19 23:56:14 +03:00
* Copyright ( C ) 2006 Thomas Tuttle < linux - kernel @ ttuttle . net >
2005-04-17 02:20:36 +04:00
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* 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 , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/list.h>
2007-11-05 19:43:32 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2007-08-20 14:23:53 +04:00
# include <linux/input.h>
2006-11-10 21:40:34 +03:00
# include <linux/backlight.h>
2008-01-17 10:51:22 +03:00
# include <linux/thermal.h>
2007-05-09 17:07:05 +04:00
# include <linux/video_output.h>
2008-12-12 00:24:52 +03:00
# include <linux/sort.h>
2009-03-20 00:35:39 +03:00
# include <linux/pci.h>
# include <linux/pci_ids.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# define ACPI_VIDEO_CLASS "video"
# define ACPI_VIDEO_BUS_NAME "Video Bus"
# define ACPI_VIDEO_DEVICE_NAME "Video Device"
# define ACPI_VIDEO_NOTIFY_SWITCH 0x80
# define ACPI_VIDEO_NOTIFY_PROBE 0x81
# define ACPI_VIDEO_NOTIFY_CYCLE 0x82
# define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83
# define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84
2006-12-19 23:56:14 +03:00
# define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85
# define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
# define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
# define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88
# define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89
2005-04-17 02:20:36 +04:00
2006-11-10 21:40:34 +03:00
# define MAX_NAME_LEN 20
2005-04-17 02:20:36 +04:00
2007-01-04 07:40:53 +03:00
# define ACPI_VIDEO_DISPLAY_CRT 1
# define ACPI_VIDEO_DISPLAY_TV 2
# define ACPI_VIDEO_DISPLAY_DVI 3
# define ACPI_VIDEO_DISPLAY_LCD 4
2005-04-17 02:20:36 +04:00
# define _COMPONENT ACPI_VIDEO_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " video " ) ;
2005-04-17 02:20:36 +04:00
2007-02-13 06:42:12 +03:00
MODULE_AUTHOR ( " Bruno Ducrot " ) ;
2007-02-13 07:50:02 +03:00
MODULE_DESCRIPTION ( " ACPI Video Driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
2008-01-25 09:47:49 +03:00
static int brightness_switch_enabled = 1 ;
module_param ( brightness_switch_enabled , bool , 0644 ) ;
2009-06-16 07:23:13 +04:00
static int register_count = 0 ;
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_add ( struct acpi_device * device ) ;
static int acpi_video_bus_remove ( struct acpi_device * device , int type ) ;
2008-02-05 10:31:24 +03:00
static int acpi_video_resume ( struct acpi_device * device ) ;
2009-04-07 19:37:11 +04:00
static void acpi_video_bus_notify ( struct acpi_device * device , u32 event ) ;
2005-04-17 02:20:36 +04:00
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id video_device_ids [ ] = {
{ ACPI_VIDEO_HID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , video_device_ids ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_video_bus = {
2007-02-13 07:33:40 +03:00
. name = " video " ,
2005-04-17 02:20:36 +04:00
. class = ACPI_VIDEO_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = video_device_ids ,
2005-04-17 02:20:36 +04:00
. ops = {
. add = acpi_video_bus_add ,
. remove = acpi_video_bus_remove ,
2008-02-05 10:31:24 +03:00
. resume = acpi_video_resume ,
2009-04-07 19:37:11 +04:00
. notify = acpi_video_bus_notify ,
2005-08-05 08:44:28 +04:00
} ,
2005-04-17 02:20:36 +04:00
} ;
struct acpi_video_bus_flags {
2005-08-05 08:44:28 +04:00
u8 multihead : 1 ; /* can switch video heads */
u8 rom : 1 ; /* can retrieve a video rom */
u8 post : 1 ; /* can configure the head to */
u8 reserved : 5 ;
2005-04-17 02:20:36 +04:00
} ;
struct acpi_video_bus_cap {
2005-08-05 08:44:28 +04:00
u8 _DOS : 1 ; /*Enable/Disable output switching */
u8 _DOD : 1 ; /*Enumerate all devices attached to display adapter */
u8 _ROM : 1 ; /*Get ROM Data */
u8 _GPD : 1 ; /*Get POST Device */
u8 _SPD : 1 ; /*Set POST Device */
u8 _VPO : 1 ; /*Video POST Options */
u8 reserved : 2 ;
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
struct acpi_video_device_attrib {
u32 display_index : 4 ; /* A zero-based instance of the Display */
2007-02-20 18:38:40 +03:00
u32 display_port_attachment : 4 ; /*This field differentiates the display type */
2005-08-05 08:44:28 +04:00
u32 display_type : 4 ; /*Describe the specific type in use */
2007-02-20 18:38:40 +03:00
u32 vendor_specific : 4 ; /*Chipset Vendor Specific */
2005-08-05 08:44:28 +04:00
u32 bios_can_detect : 1 ; /*BIOS can detect the device */
u32 depend_on_vga : 1 ; /*Non-VGA output device whose power is related to
the VGA device . */
u32 pipe_id : 3 ; /*For VGA multiple-head devices. */
u32 reserved : 10 ; /*Must be 0 */
u32 device_id_scheme : 1 ; /*Device ID Scheme */
2005-04-17 02:20:36 +04:00
} ;
struct acpi_video_enumerated_device {
union {
u32 int_val ;
2005-08-05 08:44:28 +04:00
struct acpi_video_device_attrib attrib ;
2005-04-17 02:20:36 +04:00
} value ;
struct acpi_video_device * bind_info ;
} ;
struct acpi_video_bus {
2006-05-20 00:54:40 +04:00
struct acpi_device * device ;
2005-08-05 08:44:28 +04:00
u8 dos_setting ;
2005-04-17 02:20:36 +04:00
struct acpi_video_enumerated_device * attached_array ;
2005-08-05 08:44:28 +04:00
u8 attached_count ;
struct acpi_video_bus_cap cap ;
2005-04-17 02:20:36 +04:00
struct acpi_video_bus_flags flags ;
2005-08-05 08:44:28 +04:00
struct list_head video_device_list ;
2007-11-05 19:43:32 +03:00
struct mutex device_list_lock ; /* protects video_device_list */
2005-08-05 08:44:28 +04:00
struct proc_dir_entry * dir ;
2007-08-20 14:23:53 +04:00
struct input_dev * input ;
char phys [ 32 ] ; /* for input device */
2005-04-17 02:20:36 +04:00
} ;
struct acpi_video_device_flags {
2005-08-05 08:44:28 +04:00
u8 crt : 1 ;
u8 lcd : 1 ;
u8 tvout : 1 ;
2007-01-04 07:40:53 +03:00
u8 dvi : 1 ;
2005-08-05 08:44:28 +04:00
u8 bios : 1 ;
u8 unknown : 1 ;
2007-01-04 07:40:53 +03:00
u8 reserved : 2 ;
2005-04-17 02:20:36 +04:00
} ;
struct acpi_video_device_cap {
2005-08-05 08:44:28 +04:00
u8 _ADR : 1 ; /*Return the unique ID */
u8 _BCL : 1 ; /*Query list of brightness control levels supported */
u8 _BCM : 1 ; /*Set the brightness level */
2006-11-10 21:40:34 +03:00
u8 _BQC : 1 ; /* Get current brightness level */
2009-03-18 11:27:18 +03:00
u8 _BCQ : 1 ; /* Some buggy BIOS uses _BCQ instead of _BQC */
2005-08-05 08:44:28 +04:00
u8 _DDC : 1 ; /*Return the EDID for this device */
u8 _DCS : 1 ; /*Return status of output device */
u8 _DGS : 1 ; /*Query graphics state */
u8 _DSS : 1 ; /*Device state set */
2005-04-17 02:20:36 +04:00
} ;
2009-03-18 11:27:12 +03:00
struct acpi_video_brightness_flags {
u8 _BCL_no_ac_battery_levels : 1 ; /* no AC/Battery levels in _BCL */
2009-03-18 11:27:14 +03:00
u8 _BCL_reversed : 1 ; /* _BCL package is in a reversed order*/
2009-03-18 11:27:16 +03:00
u8 _BCL_use_index : 1 ; /* levels in _BCL are index values */
u8 _BCM_use_index : 1 ; /* input of _BCM is an index value */
u8 _BQC_use_index : 1 ; /* _BQC returns an index value */
2009-03-18 11:27:12 +03:00
} ;
2005-04-17 02:20:36 +04:00
struct acpi_video_device_brightness {
2005-08-05 08:44:28 +04:00
int curr ;
int count ;
int * levels ;
2009-03-18 11:27:12 +03:00
struct acpi_video_brightness_flags flags ;
2005-04-17 02:20:36 +04:00
} ;
struct acpi_video_device {
2005-08-05 08:44:28 +04:00
unsigned long device_id ;
struct acpi_video_device_flags flags ;
struct acpi_video_device_cap cap ;
struct list_head entry ;
struct acpi_video_bus * video ;
struct acpi_device * dev ;
2005-04-17 02:20:36 +04:00
struct acpi_video_device_brightness * brightness ;
2006-11-10 21:40:34 +03:00
struct backlight_device * backlight ;
2009-08-30 07:03:16 +04:00
struct thermal_cooling_device * cooling_dev ;
2007-05-09 17:07:05 +04:00
struct output_device * output_dev ;
2005-04-17 02:20:36 +04:00
} ;
/* bus */
static int acpi_video_bus_info_open_fs ( struct inode * inode , struct file * file ) ;
2009-01-12 02:07:55 +03:00
static const struct file_operations acpi_video_bus_info_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_bus_info_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
static int acpi_video_bus_ROM_open_fs ( struct inode * inode , struct file * file ) ;
2009-01-12 02:07:55 +03:00
static const struct file_operations acpi_video_bus_ROM_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_bus_ROM_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_POST_info_open_fs ( struct inode * inode ,
struct file * file ) ;
2009-01-12 02:07:55 +03:00
static const struct file_operations acpi_video_bus_POST_info_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_bus_POST_info_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
static int acpi_video_bus_POST_open_fs ( struct inode * inode , struct file * file ) ;
2009-04-04 11:33:45 +04:00
static ssize_t acpi_video_bus_write_POST ( struct file * file ,
2009-01-12 02:08:19 +03:00
const char __user * buffer , size_t count , loff_t * data ) ;
static const struct file_operations acpi_video_bus_POST_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_bus_POST_open_fs ,
. read = seq_read ,
2009-01-12 02:08:19 +03:00
. write = acpi_video_bus_write_POST ,
2005-08-05 08:44:28 +04:00
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
static int acpi_video_bus_DOS_open_fs ( struct inode * inode , struct file * file ) ;
2009-04-04 11:33:45 +04:00
static ssize_t acpi_video_bus_write_DOS ( struct file * file ,
2009-01-12 02:08:19 +03:00
const char __user * buffer , size_t count , loff_t * data ) ;
static const struct file_operations acpi_video_bus_DOS_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_bus_DOS_open_fs ,
. read = seq_read ,
2009-01-12 02:08:19 +03:00
. write = acpi_video_bus_write_DOS ,
2005-08-05 08:44:28 +04:00
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
/* device */
2005-08-05 08:44:28 +04:00
static int acpi_video_device_info_open_fs ( struct inode * inode ,
struct file * file ) ;
2009-01-12 02:07:55 +03:00
static const struct file_operations acpi_video_device_info_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_device_info_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static int acpi_video_device_state_open_fs ( struct inode * inode ,
struct file * file ) ;
2009-04-04 11:33:45 +04:00
static ssize_t acpi_video_device_write_state ( struct file * file ,
2009-01-12 02:08:19 +03:00
const char __user * buffer , size_t count , loff_t * data ) ;
static const struct file_operations acpi_video_device_state_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_device_state_open_fs ,
. read = seq_read ,
2009-01-12 02:08:19 +03:00
. write = acpi_video_device_write_state ,
2005-08-05 08:44:28 +04:00
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static int acpi_video_device_brightness_open_fs ( struct inode * inode ,
struct file * file ) ;
2009-04-04 11:33:45 +04:00
static ssize_t acpi_video_device_write_brightness ( struct file * file ,
2009-01-12 02:08:19 +03:00
const char __user * buffer , size_t count , loff_t * data ) ;
2005-04-17 02:20:36 +04:00
static struct file_operations acpi_video_device_brightness_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_device_brightness_open_fs ,
. read = seq_read ,
2009-01-12 02:08:19 +03:00
. write = acpi_video_device_write_brightness ,
2005-08-05 08:44:28 +04:00
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static int acpi_video_device_EDID_open_fs ( struct inode * inode ,
struct file * file ) ;
2009-01-12 02:07:55 +03:00
static const struct file_operations acpi_video_device_EDID_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_video_device_EDID_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
2009-01-12 02:08:19 +03:00
static const char device_decode [ ] [ 30 ] = {
2005-04-17 02:20:36 +04:00
" motherboard VGA device " ,
" PCI VGA device " ,
" AGP VGA device " ,
" UNKNOWN " ,
} ;
2005-08-05 08:44:28 +04:00
static void acpi_video_device_notify ( acpi_handle handle , u32 event , void * data ) ;
static void acpi_video_device_rebind ( struct acpi_video_bus * video ) ;
static void acpi_video_device_bind ( struct acpi_video_bus * video ,
struct acpi_video_device * device ) ;
2005-04-17 02:20:36 +04:00
static int acpi_video_device_enumerate ( struct acpi_video_bus * video ) ;
2006-11-10 21:40:34 +03:00
static int acpi_video_device_lcd_set_level ( struct acpi_video_device * device ,
int level ) ;
static int acpi_video_device_lcd_get_level_current (
struct acpi_video_device * device ,
2008-10-10 10:22:59 +04:00
unsigned long long * level ) ;
2005-08-05 08:44:28 +04:00
static int acpi_video_get_next_level ( struct acpi_video_device * device ,
u32 level_current , u32 event ) ;
2009-03-18 11:27:08 +03:00
static int acpi_video_switch_brightness ( struct acpi_video_device * device ,
2005-08-05 08:44:28 +04:00
int event ) ;
2007-05-09 17:07:05 +04:00
static int acpi_video_device_get_state ( struct acpi_video_device * device ,
2008-10-10 10:22:59 +04:00
unsigned long long * state ) ;
2007-05-09 17:07:05 +04:00
static int acpi_video_output_get ( struct output_device * od ) ;
static int acpi_video_device_set_state ( struct acpi_video_device * device , int state ) ;
2005-04-17 02:20:36 +04:00
2006-11-10 21:40:34 +03:00
/*backlight device sysfs support*/
static int acpi_video_get_brightness ( struct backlight_device * bd )
{
2008-10-10 10:22:59 +04:00
unsigned long long cur_level ;
2007-12-26 05:03:26 +03:00
int i ;
2006-11-10 21:40:34 +03:00
struct acpi_video_device * vd =
2007-07-09 15:17:24 +04:00
( struct acpi_video_device * ) bl_get_data ( bd ) ;
2009-03-18 11:27:08 +03:00
if ( acpi_video_device_lcd_get_level_current ( vd , & cur_level ) )
return - EINVAL ;
2007-12-26 05:03:26 +03:00
for ( i = 2 ; i < vd - > brightness - > count ; i + + ) {
if ( vd - > brightness - > levels [ i ] = = cur_level )
/* The first two entries are special - see page 575
of the ACPI spec 3.0 */
return i - 2 ;
}
return 0 ;
2006-11-10 21:40:34 +03:00
}
static int acpi_video_set_brightness ( struct backlight_device * bd )
{
2009-03-18 11:27:10 +03:00
int request_level = bd - > props . brightness + 2 ;
2006-11-10 21:40:34 +03:00
struct acpi_video_device * vd =
2007-07-09 15:17:24 +04:00
( struct acpi_video_device * ) bl_get_data ( bd ) ;
2009-03-18 11:27:10 +03:00
return acpi_video_device_lcd_set_level ( vd ,
vd - > brightness - > levels [ request_level ] ) ;
2006-11-10 21:40:34 +03:00
}
2007-02-11 02:07:48 +03:00
static struct backlight_ops acpi_backlight_ops = {
. get_brightness = acpi_video_get_brightness ,
. update_status = acpi_video_set_brightness ,
} ;
2007-05-09 17:07:05 +04:00
/*video output device sysfs support*/
static int acpi_video_output_get ( struct output_device * od )
{
2008-10-10 10:22:59 +04:00
unsigned long long state ;
2007-05-09 17:07:05 +04:00
struct acpi_video_device * vd =
2007-08-08 09:28:47 +04:00
( struct acpi_video_device * ) dev_get_drvdata ( & od - > dev ) ;
2007-05-09 17:07:05 +04:00
acpi_video_device_get_state ( vd , & state ) ;
return ( int ) state ;
}
static int acpi_video_output_set ( struct output_device * od )
{
unsigned long state = od - > request_state ;
struct acpi_video_device * vd =
2007-08-08 09:28:47 +04:00
( struct acpi_video_device * ) dev_get_drvdata ( & od - > dev ) ;
2007-05-09 17:07:05 +04:00
return acpi_video_device_set_state ( vd , state ) ;
}
static struct output_properties acpi_output_properties = {
. set_state = acpi_video_output_set ,
. get_status = acpi_video_output_get ,
} ;
2008-01-17 10:51:22 +03:00
/* thermal cooling device callbacks */
2009-08-30 07:03:16 +04:00
static int video_get_max_state ( struct thermal_cooling_device * cooling_dev , unsigned
2008-11-27 20:48:13 +03:00
long * state )
2008-01-17 10:51:22 +03:00
{
2009-08-30 07:03:16 +04:00
struct acpi_device * device = cooling_dev - > devdata ;
2008-01-17 10:51:22 +03:00
struct acpi_video_device * video = acpi_driver_data ( device ) ;
2008-11-27 20:48:13 +03:00
* state = video - > brightness - > count - 3 ;
return 0 ;
2008-01-17 10:51:22 +03:00
}
2009-08-30 07:03:16 +04:00
static int video_get_cur_state ( struct thermal_cooling_device * cooling_dev , unsigned
2008-11-27 20:48:13 +03:00
long * state )
2008-01-17 10:51:22 +03:00
{
2009-08-30 07:03:16 +04:00
struct acpi_device * device = cooling_dev - > devdata ;
2008-01-17 10:51:22 +03:00
struct acpi_video_device * video = acpi_driver_data ( device ) ;
2008-10-10 10:22:59 +04:00
unsigned long long level ;
2008-11-27 20:48:13 +03:00
int offset ;
2008-01-17 10:51:22 +03:00
2009-03-18 11:27:08 +03:00
if ( acpi_video_device_lcd_get_level_current ( video , & level ) )
return - EINVAL ;
2008-11-27 20:48:13 +03:00
for ( offset = 2 ; offset < video - > brightness - > count ; offset + + )
if ( level = = video - > brightness - > levels [ offset ] ) {
* state = video - > brightness - > count - offset - 1 ;
return 0 ;
}
2008-01-17 10:51:22 +03:00
return - EINVAL ;
}
static int
2009-08-30 07:03:16 +04:00
video_set_cur_state ( struct thermal_cooling_device * cooling_dev , unsigned long state )
2008-01-17 10:51:22 +03:00
{
2009-08-30 07:03:16 +04:00
struct acpi_device * device = cooling_dev - > devdata ;
2008-01-17 10:51:22 +03:00
struct acpi_video_device * video = acpi_driver_data ( device ) ;
int level ;
if ( state > = video - > brightness - > count - 2 )
return - EINVAL ;
state = video - > brightness - > count - state ;
level = video - > brightness - > levels [ state - 1 ] ;
return acpi_video_device_lcd_set_level ( video , level ) ;
}
static struct thermal_cooling_device_ops video_cooling_ops = {
. get_max_state = video_get_max_state ,
. get_cur_state = video_get_cur_state ,
. set_cur_state = video_set_cur_state ,
} ;
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Video Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* device */
static int
2008-10-10 10:22:59 +04:00
acpi_video_device_query ( struct acpi_video_device * device , unsigned long long * state )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_integer ( device - > dev - > handle , " _DGS " , NULL , state ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_get_state ( struct acpi_video_device * device ,
2008-10-10 10:22:59 +04:00
unsigned long long * state )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_integer ( device - > dev - > handle , " _DCS " , NULL , state ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_set_state ( struct acpi_video_device * device , int state )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list args = { 1 , & arg0 } ;
2008-10-10 10:22:59 +04:00
unsigned long long ret ;
2005-04-17 02:20:36 +04:00
arg0 . integer . value = state ;
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_integer ( device - > dev - > handle , " _DSS " , & args , & ret ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_lcd_query_levels ( struct acpi_video_device * device ,
union acpi_object * * levels )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
2005-04-17 02:20:36 +04:00
* levels = NULL ;
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_object ( device - > dev - > handle , " _BCL " , NULL , & buffer ) ;
2005-04-17 02:20:36 +04:00
if ( ! ACPI_SUCCESS ( status ) )
2006-06-27 08:41:40 +04:00
return status ;
2005-08-05 08:44:28 +04:00
obj = ( union acpi_object * ) buffer . pointer ;
2006-03-11 18:12:00 +03:00
if ( ! obj | | ( obj - > type ! = ACPI_TYPE_PACKAGE ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Invalid _BCL data \n " ) ;
2005-04-17 02:20:36 +04:00
status = - EFAULT ;
goto err ;
}
* levels = obj ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
err :
2005-11-07 12:01:32 +03:00
kfree ( buffer . pointer ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_lcd_set_level ( struct acpi_video_device * device , int level )
2005-04-17 02:20:36 +04:00
{
2009-03-18 11:27:10 +03:00
int status ;
2005-08-05 08:44:28 +04:00
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list args = { 1 , & arg0 } ;
2008-12-31 05:58:48 +03:00
int state ;
2005-04-17 02:20:36 +04:00
arg0 . integer . value = level ;
2009-03-18 11:27:10 +03:00
status = acpi_evaluate_object ( device - > dev - > handle , " _BCM " ,
& args , NULL ) ;
if ( ACPI_FAILURE ( status ) ) {
ACPI_ERROR ( ( AE_INFO , " Evaluating _BCM failed " ) ) ;
return - EIO ;
}
2007-09-03 16:29:58 +04:00
device - > brightness - > curr = level ;
2008-12-31 05:58:48 +03:00
for ( state = 2 ; state < device - > brightness - > count ; state + + )
2009-03-18 11:27:10 +03:00
if ( level = = device - > brightness - > levels [ state ] ) {
2009-03-18 11:27:16 +03:00
if ( device - > backlight )
device - > backlight - > props . brightness = state - 2 ;
2009-03-18 11:27:10 +03:00
return 0 ;
}
2008-12-31 05:58:48 +03:00
2009-03-18 11:27:10 +03:00
ACPI_ERROR ( ( AE_INFO , " Current brightness invalid " ) ) ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2009-04-24 20:13:18 +04:00
/*
* For some buggy _BQC methods , we need to add a constant value to
* the _BQC return value to get the actual current brightness level
*/
static int bqc_offset_aml_bug_workaround ;
static int __init video_set_bqc_offset ( const struct dmi_system_id * d )
{
bqc_offset_aml_bug_workaround = 9 ;
return 0 ;
}
static struct dmi_system_id video_dmi_table [ ] __initdata = {
/*
* Broken _BQC workaround http : //bugzilla.kernel.org/show_bug.cgi?id=13121
*/
{
. callback = video_set_bqc_offset ,
. ident = " Acer Aspire 5720 " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire 5720 " ) ,
} ,
} ,
2009-05-08 05:11:56 +04:00
{
. callback = video_set_bqc_offset ,
. ident = " Acer Aspire 5710Z " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire 5710Z " ) ,
} ,
} ,
2009-05-27 07:35:34 +04:00
{
. callback = video_set_bqc_offset ,
. ident = " eMachines E510 " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " EMACHINES " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " eMachines E510 " ) ,
} ,
} ,
2009-05-19 23:08:41 +04:00
{
. callback = video_set_bqc_offset ,
. ident = " Acer Aspire 5315 " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire 5315 " ) ,
} ,
} ,
2009-06-22 07:31:18 +04:00
{
. callback = video_set_bqc_offset ,
. ident = " Acer Aspire 7720 " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire 7720 " ) ,
} ,
} ,
2009-04-24 20:13:18 +04:00
{ }
} ;
2005-04-17 02:20:36 +04:00
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_lcd_get_level_current ( struct acpi_video_device * device ,
2008-10-10 10:22:59 +04:00
unsigned long long * level )
2005-04-17 02:20:36 +04:00
{
2009-03-18 11:27:08 +03:00
acpi_status status = AE_OK ;
2009-06-24 11:17:36 +04:00
int i ;
2009-03-18 11:27:08 +03:00
2009-03-18 11:27:18 +03:00
if ( device - > cap . _BQC | | device - > cap . _BCQ ) {
char * buf = device - > cap . _BQC ? " _BQC " : " _BCQ " ;
status = acpi_evaluate_integer ( device - > dev - > handle , buf ,
2009-03-18 11:27:08 +03:00
NULL , level ) ;
if ( ACPI_SUCCESS ( status ) ) {
2009-03-18 11:27:16 +03:00
if ( device - > brightness - > flags . _BQC_use_index ) {
if ( device - > brightness - > flags . _BCL_reversed )
* level = device - > brightness - > count
- 3 - ( * level ) ;
* level = device - > brightness - > levels [ * level + 2 ] ;
}
2009-04-24 20:13:18 +04:00
* level + = bqc_offset_aml_bug_workaround ;
2009-06-24 11:17:36 +04:00
for ( i = 2 ; i < device - > brightness - > count ; i + + )
if ( device - > brightness - > levels [ i ] = = * level ) {
device - > brightness - > curr = * level ;
return 0 ;
}
/* BQC returned an invalid level. Stop using it. */
ACPI_WARNING ( ( AE_INFO , " %s returned an invalid level " ,
buf ) ) ;
device - > cap . _BQC = device - > cap . _BCQ = 0 ;
2009-03-18 11:27:08 +03:00
} else {
/* Fixme:
* should we return an error or ignore this failure ?
* dev - > brightness - > curr is a cached value which stores
* the correct current backlight level in most cases .
* ACPI video backlight still works w / buggy _BQC .
* http : //bugzilla.kernel.org/show_bug.cgi?id=12233
*/
2009-03-18 11:27:18 +03:00
ACPI_WARNING ( ( AE_INFO , " Evaluating %s failed " , buf ) ) ;
device - > cap . _BQC = device - > cap . _BCQ = 0 ;
2009-03-18 11:27:08 +03:00
}
}
2007-09-03 16:29:58 +04:00
* level = device - > brightness - > curr ;
2009-03-18 11:27:08 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_EDID ( struct acpi_video_device * device ,
union acpi_object * * edid , ssize_t length )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * obj ;
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list args = { 1 , & arg0 } ;
2005-04-17 02:20:36 +04:00
* edid = NULL ;
if ( ! device )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
if ( length = = 128 )
arg0 . integer . value = 1 ;
else if ( length = = 256 )
arg0 . integer . value = 2 ;
else
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_object ( device - > dev - > handle , " _DDC " , & args , & buffer ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
obj = buffer . pointer ;
2005-04-17 02:20:36 +04:00
if ( obj & & obj - > type = = ACPI_TYPE_BUFFER )
* edid = obj ;
else {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Invalid _DDC data \n " ) ;
2005-04-17 02:20:36 +04:00
status = - EFAULT ;
kfree ( obj ) ;
}
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
/* bus */
static int
2005-08-05 08:44:28 +04:00
acpi_video_bus_set_POST ( struct acpi_video_bus * video , unsigned long option )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2008-10-10 10:22:59 +04:00
unsigned long long tmp ;
2005-08-05 08:44:28 +04:00
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list args = { 1 , & arg0 } ;
2005-04-17 02:20:36 +04:00
arg0 . integer . value = option ;
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_integer ( video - > device - > handle , " _SPD " , & args , & tmp ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_SUCCESS ( status ) )
2005-08-05 08:44:28 +04:00
status = tmp ? ( - EINVAL ) : ( AE_OK ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int
2008-10-10 10:22:59 +04:00
acpi_video_bus_get_POST ( struct acpi_video_bus * video , unsigned long long * id )
2005-04-17 02:20:36 +04:00
{
int status ;
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_integer ( video - > device - > handle , " _GPD " , NULL , id ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_bus_POST_options ( struct acpi_video_bus * video ,
2008-10-10 10:22:59 +04:00
unsigned long long * options )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_integer ( video - > device - > handle , " _VPO " , NULL , options ) ;
2005-04-17 02:20:36 +04:00
* options & = 3 ;
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
/*
* Arg :
* video : video bus device pointer
* bios_flag :
* 0. The system BIOS should NOT automatically switch ( toggle )
* the active display output .
* 1. The system BIOS should automatically switch ( toggle ) the
2007-02-20 18:38:40 +03:00
* active display output . No switch event .
2005-04-17 02:20:36 +04:00
* 2. The _DGS value should be locked .
* 3. The system BIOS should not automatically switch ( toggle ) the
* active display output , but instead generate the display switch
* event notify code .
* lcd_flag :
* 0. The system BIOS should automatically control the brightness level
2007-02-20 18:38:40 +03:00
* of the LCD when the power changes from AC to DC
2005-04-17 02:20:36 +04:00
* 1. The system BIOS should NOT automatically control the brightness
2007-02-20 18:38:40 +03:00
* level of the LCD when the power changes from AC to DC .
2005-04-17 02:20:36 +04:00
* Return Value :
* - 1 wrong arg .
*/
static int
2005-08-05 08:44:28 +04:00
acpi_video_bus_DOS ( struct acpi_video_bus * video , int bios_flag , int lcd_flag )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_integer status = 0 ;
union acpi_object arg0 = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list args = { 1 , & arg0 } ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
if ( bios_flag < 0 | | bios_flag > 3 | | lcd_flag < 0 | | lcd_flag > 1 ) {
2005-04-17 02:20:36 +04:00
status = - 1 ;
goto Failed ;
}
arg0 . integer . value = ( lcd_flag < < 2 ) | bios_flag ;
video - > dos_setting = arg0 . integer . value ;
2006-05-20 00:54:48 +04:00
acpi_evaluate_object ( video - > device - > handle , " _DOS " , & args , NULL ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
Failed :
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
2008-12-12 00:24:52 +03:00
/*
* Simple comparison function used to sort backlight levels .
*/
static int
acpi_video_cmp_level ( const void * a , const void * b )
{
return * ( int * ) a - * ( int * ) b ;
}
2005-04-17 02:20:36 +04:00
/*
* Arg :
* device : video output device ( LCD , CRT , . . )
*
* Return Value :
2008-06-24 01:50:42 +04:00
* Maximum brightness level
*
* Allocate and initialize device - > brightness .
*/
static int
acpi_video_init_brightness ( struct acpi_video_device * device )
{
union acpi_object * obj = NULL ;
2009-03-18 11:27:12 +03:00
int i , max_level = 0 , count = 0 , level_ac_battery = 0 ;
2009-03-18 11:27:16 +03:00
unsigned long long level , level_old ;
2008-06-24 01:50:42 +04:00
union acpi_object * o ;
struct acpi_video_device_brightness * br = NULL ;
2009-03-18 11:27:16 +03:00
int result = - EINVAL ;
2008-06-24 01:50:42 +04:00
if ( ! ACPI_SUCCESS ( acpi_video_device_lcd_query_levels ( device , & obj ) ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Could not query available "
" LCD brightness level \n " ) ) ;
goto out ;
}
if ( obj - > package . count < 2 )
goto out ;
br = kzalloc ( sizeof ( * br ) , GFP_KERNEL ) ;
if ( ! br ) {
printk ( KERN_ERR " can't allocate memory \n " ) ;
2009-03-18 11:27:16 +03:00
result = - ENOMEM ;
2008-06-24 01:50:42 +04:00
goto out ;
}
2009-03-18 11:27:12 +03:00
br - > levels = kmalloc ( ( obj - > package . count + 2 ) * sizeof * ( br - > levels ) ,
2008-06-24 01:50:42 +04:00
GFP_KERNEL ) ;
2009-03-18 11:27:16 +03:00
if ( ! br - > levels ) {
result = - ENOMEM ;
2008-06-24 01:50:42 +04:00
goto out_free ;
2009-03-18 11:27:16 +03:00
}
2008-06-24 01:50:42 +04:00
for ( i = 0 ; i < obj - > package . count ; i + + ) {
o = ( union acpi_object * ) & obj - > package . elements [ i ] ;
if ( o - > type ! = ACPI_TYPE_INTEGER ) {
printk ( KERN_ERR PREFIX " Invalid data \n " ) ;
continue ;
}
br - > levels [ count ] = ( u32 ) o - > integer . value ;
if ( br - > levels [ count ] > max_level )
max_level = br - > levels [ count ] ;
count + + ;
}
2009-03-18 11:27:12 +03:00
/*
* some buggy BIOS don ' t export the levels
* when machine is on AC / Battery in _BCL package .
* In this case , the first two elements in _BCL packages
* are also supported brightness levels that OS should take care of .
*/
2009-04-14 07:02:18 +04:00
for ( i = 2 ; i < count ; i + + ) {
if ( br - > levels [ i ] = = br - > levels [ 0 ] )
2009-03-18 11:27:12 +03:00
level_ac_battery + + ;
2009-04-14 07:02:18 +04:00
if ( br - > levels [ i ] = = br - > levels [ 1 ] )
level_ac_battery + + ;
}
2009-03-18 11:27:12 +03:00
if ( level_ac_battery < 2 ) {
level_ac_battery = 2 - level_ac_battery ;
br - > flags . _BCL_no_ac_battery_levels = 1 ;
for ( i = ( count - 1 + level_ac_battery ) ; i > = 2 ; i - - )
br - > levels [ i ] = br - > levels [ i - level_ac_battery ] ;
count + = level_ac_battery ;
} else if ( level_ac_battery > 2 )
ACPI_ERROR ( ( AE_INFO , " Too many duplicates in _BCL package \n " ) ) ;
2009-03-18 11:27:14 +03:00
/* Check if the _BCL package is in a reversed order */
if ( max_level = = br - > levels [ 2 ] ) {
br - > flags . _BCL_reversed = 1 ;
sort ( & br - > levels [ 2 ] , count - 2 , sizeof ( br - > levels [ 2 ] ) ,
acpi_video_cmp_level , NULL ) ;
} else if ( max_level ! = br - > levels [ count - 1 ] )
ACPI_ERROR ( ( AE_INFO ,
" Found unordered _BCL package \n " ) ) ;
2008-06-24 01:50:42 +04:00
br - > count = count ;
device - > brightness = br ;
2009-03-18 11:27:16 +03:00
/* Check the input/output of _BQC/_BCL/_BCM */
if ( ( max_level < 100 ) & & ( max_level < = ( count - 2 ) ) )
br - > flags . _BCL_use_index = 1 ;
/*
* _BCM is always consistent with _BCL ,
* at least for all the laptops we have ever seen .
*/
br - > flags . _BCM_use_index = br - > flags . _BCL_use_index ;
/* _BQC uses INDEX while _BCL uses VALUE in some laptops */
2009-04-09 10:24:35 +04:00
br - > curr = level_old = max_level ;
if ( ! device - > cap . _BQC )
goto set_level ;
2009-03-18 11:27:16 +03:00
result = acpi_video_device_lcd_get_level_current ( device , & level_old ) ;
if ( result )
goto out_free_levels ;
2009-04-09 10:24:35 +04:00
/*
* Set the level to maximum and check if _BQC uses indexed value
*/
result = acpi_video_device_lcd_set_level ( device , max_level ) ;
2009-03-18 11:27:16 +03:00
if ( result )
goto out_free_levels ;
result = acpi_video_device_lcd_get_level_current ( device , & level ) ;
if ( result )
goto out_free_levels ;
2009-04-09 10:24:35 +04:00
br - > flags . _BQC_use_index = ( level = = max_level ? 0 : 1 ) ;
if ( ! br - > flags . _BQC_use_index )
goto set_level ;
if ( br - > flags . _BCL_reversed )
level_old = ( br - > count - 1 ) - level_old ;
level_old = br - > levels [ level_old ] ;
set_level :
result = acpi_video_device_lcd_set_level ( device , level_old ) ;
if ( result )
goto out_free_levels ;
2009-03-18 11:27:16 +03:00
2009-03-18 11:27:12 +03:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" found %d brightness levels \n " , count - 2 ) ) ;
2008-06-24 01:50:42 +04:00
kfree ( obj ) ;
2009-03-18 11:27:16 +03:00
return result ;
2008-06-24 01:50:42 +04:00
out_free_levels :
kfree ( br - > levels ) ;
out_free :
kfree ( br ) ;
out :
device - > brightness = NULL ;
kfree ( obj ) ;
2009-03-18 11:27:16 +03:00
return result ;
2008-06-24 01:50:42 +04:00
}
/*
* Arg :
* device : video output device ( LCD , CRT , . . )
*
* Return Value :
2005-04-17 02:20:36 +04:00
* None
*
2007-02-20 18:38:40 +03:00
* Find out all required AML methods defined under the output
2005-04-17 02:20:36 +04:00
* device .
*/
2005-08-05 08:44:28 +04:00
static void acpi_video_device_find_cap ( struct acpi_video_device * device )
2005-04-17 02:20:36 +04:00
{
acpi_handle h_dummy1 ;
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _ADR " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
device - > cap . _ADR = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _BCL " , & h_dummy1 ) ) ) {
2005-08-05 08:44:28 +04:00
device - > cap . _BCL = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _BCM " , & h_dummy1 ) ) ) {
2005-08-05 08:44:28 +04:00
device - > cap . _BCM = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-11-10 21:40:34 +03:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _BQC " , & h_dummy1 ) ) )
device - > cap . _BQC = 1 ;
2009-03-18 11:27:18 +03:00
else if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _BCQ " ,
& h_dummy1 ) ) ) {
printk ( KERN_WARNING FW_BUG " _BCQ is used instead of _BQC \n " ) ;
device - > cap . _BCQ = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _DDC " , & h_dummy1 ) ) ) {
2005-08-05 08:44:28 +04:00
device - > cap . _DDC = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _DCS " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
device - > cap . _DCS = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _DGS " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
device - > cap . _DGS = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( device - > dev - > handle , " _DSS " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
device - > cap . _DSS = 1 ;
}
2009-03-18 11:27:16 +03:00
if ( acpi_video_backlight_support ( ) ) {
2008-01-17 10:51:22 +03:00
int result ;
2006-11-10 21:40:34 +03:00
static int count = 0 ;
char * name ;
2009-03-18 11:27:16 +03:00
result = acpi_video_init_brightness ( device ) ;
if ( result )
return ;
2006-11-10 21:40:34 +03:00
name = kzalloc ( MAX_NAME_LEN , GFP_KERNEL ) ;
if ( ! name )
return ;
sprintf ( name , " acpi_video%d " , count + + ) ;
device - > backlight = backlight_device_register ( name ,
2007-02-11 02:07:48 +03:00
NULL , device , & acpi_backlight_ops ) ;
2007-12-26 05:03:26 +03:00
device - > backlight - > props . max_brightness = device - > brightness - > count - 3 ;
2006-11-10 21:40:34 +03:00
kfree ( name ) ;
2008-01-17 10:51:22 +03:00
2009-06-22 07:31:14 +04:00
result = sysfs_create_link ( & device - > backlight - > dev . kobj ,
& device - > dev - > dev . kobj , " device " ) ;
if ( result )
printk ( KERN_ERR PREFIX " Create sysfs link \n " ) ;
2009-08-30 07:03:16 +04:00
device - > cooling_dev = thermal_cooling_device_register ( " LCD " ,
2008-01-17 10:51:22 +03:00
device - > dev , & video_cooling_ops ) ;
2009-08-30 07:03:16 +04:00
if ( IS_ERR ( device - > cooling_dev ) ) {
2009-08-08 11:26:25 +04:00
/*
2009-08-30 07:03:16 +04:00
* Set cooling_dev to NULL so we don ' t crash trying to
2009-08-08 11:26:25 +04:00
* free it .
* Also , why the hell we are returning early and
* not attempt to register video output if cooling
* device registration failed ?
* - - dtor
*/
2009-08-30 07:03:16 +04:00
device - > cooling_dev = NULL ;
2008-02-16 02:29:18 +03:00
return ;
2009-08-08 11:26:25 +04:00
}
2008-02-16 02:29:18 +03:00
2008-05-02 08:02:41 +04:00
dev_info ( & device - > dev - > dev , " registered as cooling_device%d \n " ,
2009-08-30 07:03:16 +04:00
device - > cooling_dev - > id ) ;
2008-04-11 06:09:24 +04:00
result = sysfs_create_link ( & device - > dev - > dev . kobj ,
2009-08-30 07:03:16 +04:00
& device - > cooling_dev - > device . kobj ,
2008-04-11 06:09:24 +04:00
" thermal_cooling " ) ;
if ( result )
printk ( KERN_ERR PREFIX " Create sysfs link \n " ) ;
2009-08-30 07:03:16 +04:00
result = sysfs_create_link ( & device - > cooling_dev - > device . kobj ,
2008-04-11 06:09:24 +04:00
& device - > dev - > dev . kobj , " device " ) ;
if ( result )
printk ( KERN_ERR PREFIX " Create sysfs link \n " ) ;
2006-11-10 21:40:34 +03:00
}
2008-08-01 19:37:55 +04:00
if ( acpi_video_display_switch_support ( ) ) {
if ( device - > cap . _DCS & & device - > cap . _DSS ) {
static int count ;
char * name ;
name = kzalloc ( MAX_NAME_LEN , GFP_KERNEL ) ;
if ( ! name )
return ;
sprintf ( name , " acpi_video%d " , count + + ) ;
device - > output_dev = video_output_register ( name ,
NULL , device , & acpi_output_properties ) ;
kfree ( name ) ;
}
2007-05-09 17:07:05 +04:00
}
2005-04-17 02:20:36 +04:00
}
/*
* Arg :
* device : video output device ( VGA )
*
* Return Value :
* None
*
2007-02-20 18:38:40 +03:00
* Find out all required AML methods defined under the video bus device .
2005-04-17 02:20:36 +04:00
*/
2005-08-05 08:44:28 +04:00
static void acpi_video_bus_find_cap ( struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_handle h_dummy1 ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( video - > device - > handle , " _DOS " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
video - > cap . _DOS = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( video - > device - > handle , " _DOD " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
video - > cap . _DOD = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( video - > device - > handle , " _ROM " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
video - > cap . _ROM = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( video - > device - > handle , " _GPD " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
video - > cap . _GPD = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( video - > device - > handle , " _SPD " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
video - > cap . _SPD = 1 ;
}
2006-05-20 00:54:48 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( video - > device - > handle , " _VPO " , & h_dummy1 ) ) ) {
2005-04-17 02:20:36 +04:00
video - > cap . _VPO = 1 ;
}
}
/*
* Check whether the video bus device has required AML method to
* support the desired features
*/
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_check ( struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = - ENOENT ;
2009-06-10 23:56:00 +04:00
struct pci_dev * dev ;
2005-04-17 02:20:36 +04:00
if ( ! video )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2009-06-10 23:56:00 +04:00
dev = acpi_get_pci_dev ( video - > device - > handle ) ;
2008-08-01 19:37:54 +04:00
if ( ! dev )
return - ENODEV ;
2009-06-10 23:56:00 +04:00
pci_dev_put ( dev ) ;
2008-08-01 19:37:54 +04:00
2005-04-17 02:20:36 +04:00
/* Since there is no HID, CID and so on for VGA driver, we have
* to check well known required nodes .
*/
2007-02-20 18:38:40 +03:00
/* Does this device support video switching? */
2005-08-05 08:44:28 +04:00
if ( video - > cap . _DOS ) {
2005-04-17 02:20:36 +04:00
video - > flags . multihead = 1 ;
status = 0 ;
}
2007-02-20 18:38:40 +03:00
/* Does this device support retrieving a video ROM? */
2005-08-05 08:44:28 +04:00
if ( video - > cap . _ROM ) {
2005-04-17 02:20:36 +04:00
video - > flags . rom = 1 ;
status = 0 ;
}
2007-02-20 18:38:40 +03:00
/* Does this device support configuring which video device to POST? */
2005-08-05 08:44:28 +04:00
if ( video - > cap . _GPD & & video - > cap . _SPD & & video - > cap . _VPO ) {
2005-04-17 02:20:36 +04:00
video - > flags . post = 1 ;
status = 0 ;
}
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
FS Interface ( / proc )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static struct proc_dir_entry * acpi_video_dir ;
2005-04-17 02:20:36 +04:00
/* video devices */
2005-08-05 08:44:28 +04:00
static int acpi_video_device_info_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_device * dev = seq - > private ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
goto end ;
seq_printf ( seq , " device_id: 0x%04x \n " , ( u32 ) dev - > device_id ) ;
seq_printf ( seq , " type: " ) ;
if ( dev - > flags . crt )
seq_printf ( seq , " CRT \n " ) ;
else if ( dev - > flags . lcd )
seq_printf ( seq , " LCD \n " ) ;
else if ( dev - > flags . tvout )
seq_printf ( seq , " TVOUT \n " ) ;
2007-01-04 07:40:53 +03:00
else if ( dev - > flags . dvi )
seq_printf ( seq , " DVI \n " ) ;
2005-04-17 02:20:36 +04:00
else
seq_printf ( seq , " UNKNOWN \n " ) ;
2005-08-05 08:44:28 +04:00
seq_printf ( seq , " known by bios: %s \n " , dev - > flags . bios ? " yes " : " no " ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_info_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
return single_open ( file , acpi_video_device_info_seq_show ,
PDE ( inode ) - > data ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_video_device_state_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2006-10-01 02:28:50 +04:00
struct acpi_video_device * dev = seq - > private ;
2008-10-10 10:22:59 +04:00
unsigned long long state ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
goto end ;
status = acpi_video_device_get_state ( dev , & state ) ;
seq_printf ( seq , " state: " ) ;
if ( ACPI_SUCCESS ( status ) )
2008-10-10 10:22:59 +04:00
seq_printf ( seq , " 0x%02llx \n " , state ) ;
2005-04-17 02:20:36 +04:00
else
seq_printf ( seq , " <not supported> \n " ) ;
status = acpi_video_device_query ( dev , & state ) ;
seq_printf ( seq , " query: " ) ;
if ( ACPI_SUCCESS ( status ) )
2008-10-10 10:22:59 +04:00
seq_printf ( seq , " 0x%02llx \n " , state ) ;
2005-04-17 02:20:36 +04:00
else
seq_printf ( seq , " <not supported> \n " ) ;
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_state_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
return single_open ( file , acpi_video_device_state_seq_show ,
PDE ( inode ) - > data ) ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_video_device_write_state ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * data )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2006-10-01 02:28:50 +04:00
struct seq_file * m = file - > private_data ;
struct acpi_video_device * dev = m - > private ;
2005-08-05 08:44:28 +04:00
char str [ 12 ] = { 0 } ;
u32 state = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | count + 1 > sizeof str )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( str , buffer , count ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
str [ count ] = 0 ;
state = simple_strtoul ( str , NULL , 0 ) ;
2005-08-05 08:44:28 +04:00
state & = ( ( 1ul < < 31 ) | ( 1ul < < 30 ) | ( 1ul < < 0 ) ) ;
2005-04-17 02:20:36 +04:00
status = acpi_video_device_set_state ( dev , state ) ;
if ( status )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_brightness_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_device * dev = seq - > private ;
2005-08-05 08:44:28 +04:00
int i ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! dev - > brightness ) {
seq_printf ( seq , " <not supported> \n " ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
seq_printf ( seq , " levels: " ) ;
2009-02-02 06:33:41 +03:00
for ( i = 2 ; i < dev - > brightness - > count ; i + + )
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " %d " , dev - > brightness - > levels [ i ] ) ;
seq_printf ( seq , " \n current: %d \n " , dev - > brightness - > curr ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_brightness_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
return single_open ( file , acpi_video_device_brightness_seq_show ,
PDE ( inode ) - > data ) ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_video_device_write_brightness ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * data )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct seq_file * m = file - > private_data ;
struct acpi_video_device * dev = m - > private ;
2007-11-02 15:47:53 +03:00
char str [ 5 ] = { 0 } ;
2005-08-05 08:44:28 +04:00
unsigned int level = 0 ;
int i ;
2005-04-17 02:20:36 +04:00
2005-11-08 13:27:00 +03:00
if ( ! dev | | ! dev - > brightness | | count + 1 > sizeof str )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( str , buffer , count ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
str [ count ] = 0 ;
level = simple_strtoul ( str , NULL , 0 ) ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
if ( level > 100 )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2007-02-20 18:38:40 +03:00
/* validate through the list of available levels */
2009-02-02 06:33:41 +03:00
for ( i = 2 ; i < dev - > brightness - > count ; i + + )
2005-04-17 02:20:36 +04:00
if ( level = = dev - > brightness - > levels [ i ] ) {
2009-03-18 11:27:10 +03:00
if ( ! acpi_video_device_lcd_set_level ( dev , level ) )
return count ;
2005-04-17 02:20:36 +04:00
break ;
}
2009-03-18 11:27:10 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_device_EDID_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_device * dev = seq - > private ;
2005-08-05 08:44:28 +04:00
int status ;
int i ;
union acpi_object * edid = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
goto out ;
2005-08-05 08:44:28 +04:00
status = acpi_video_device_EDID ( dev , & edid , 128 ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
status = acpi_video_device_EDID ( dev , & edid , 256 ) ;
2005-04-17 02:20:36 +04:00
}
if ( ACPI_FAILURE ( status ) ) {
goto out ;
}
if ( edid & & edid - > type = = ACPI_TYPE_BUFFER ) {
for ( i = 0 ; i < edid - > buffer . length ; i + + )
seq_putc ( seq , edid - > buffer . pointer [ i ] ) ;
}
2005-08-05 08:44:28 +04:00
out :
2005-04-17 02:20:36 +04:00
if ( ! edid )
seq_printf ( seq , " <not supported> \n " ) ;
else
kfree ( edid ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_device_EDID_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
return single_open ( file , acpi_video_device_EDID_seq_show ,
PDE ( inode ) - > data ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_video_device_add_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2007-11-05 19:43:34 +03:00
struct proc_dir_entry * entry , * device_dir ;
2005-04-17 02:20:36 +04:00
struct acpi_video_device * vid_dev ;
2006-10-01 02:28:50 +04:00
vid_dev = acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
if ( ! vid_dev )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:34 +03:00
device_dir = proc_mkdir ( acpi_device_bid ( device ) ,
vid_dev - > video - > dir ) ;
if ( ! device_dir )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
/* 'info' [R] */
2008-05-01 04:10:02 +04:00
entry = proc_create_data ( " info " , S_IRUGO , device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_device_info_fops , acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_dir ;
2005-04-17 02:20:36 +04:00
/* 'state' [R/W] */
2008-04-29 12:02:27 +04:00
entry = proc_create_data ( " state " , S_IFREG | S_IRUGO | S_IWUSR ,
2008-05-01 04:10:02 +04:00
device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_device_state_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_info ;
2005-04-17 02:20:36 +04:00
/* 'brightness' [R/W] */
2008-04-29 12:02:27 +04:00
entry = proc_create_data ( " brightness " , S_IFREG | S_IRUGO | S_IWUSR ,
2008-05-01 04:10:02 +04:00
device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_device_brightness_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_state ;
2005-04-17 02:20:36 +04:00
/* 'EDID' [R] */
2008-05-01 04:10:02 +04:00
entry = proc_create_data ( " EDID " , S_IRUGO , device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_device_EDID_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_brightness ;
2005-04-17 02:20:36 +04:00
2008-05-01 04:10:02 +04:00
acpi_device_dir ( device ) = device_dir ;
2006-06-27 08:41:40 +04:00
return 0 ;
2007-11-05 19:43:34 +03:00
err_remove_brightness :
remove_proc_entry ( " brightness " , device_dir ) ;
err_remove_state :
remove_proc_entry ( " state " , device_dir ) ;
err_remove_info :
remove_proc_entry ( " info " , device_dir ) ;
err_remove_dir :
remove_proc_entry ( acpi_device_bid ( device ) , vid_dev - > video - > dir ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_device_remove_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
struct acpi_video_device * vid_dev ;
2007-11-05 19:43:34 +03:00
struct proc_dir_entry * device_dir ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
vid_dev = acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
if ( ! vid_dev | | ! vid_dev - > video | | ! vid_dev - > video - > dir )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:34 +03:00
device_dir = acpi_device_dir ( device ) ;
if ( device_dir ) {
remove_proc_entry ( " info " , device_dir ) ;
remove_proc_entry ( " state " , device_dir ) ;
remove_proc_entry ( " brightness " , device_dir ) ;
remove_proc_entry ( " EDID " , device_dir ) ;
2005-08-05 08:44:28 +04:00
remove_proc_entry ( acpi_device_bid ( device ) , vid_dev - > video - > dir ) ;
2005-04-17 02:20:36 +04:00
acpi_device_dir ( device ) = NULL ;
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* video bus */
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_info_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_bus * video = seq - > private ;
2005-04-17 02:20:36 +04:00
if ( ! video )
goto end ;
seq_printf ( seq , " Switching heads: %s \n " ,
2005-08-05 08:44:28 +04:00
video - > flags . multihead ? " yes " : " no " ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " Video ROM: %s \n " ,
2005-08-05 08:44:28 +04:00
video - > flags . rom ? " yes " : " no " ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " Device to be POSTed on boot: %s \n " ,
2005-08-05 08:44:28 +04:00
video - > flags . post ? " yes " : " no " ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_info_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
return single_open ( file , acpi_video_bus_info_seq_show ,
PDE ( inode ) - > data ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_ROM_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_bus * video = seq - > private ;
2005-04-17 02:20:36 +04:00
if ( ! video )
goto end ;
2008-03-06 05:24:51 +03:00
printk ( KERN_INFO PREFIX " Please implement %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " <TODO> \n " ) ;
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_ROM_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
return single_open ( file , acpi_video_bus_ROM_seq_show , PDE ( inode ) - > data ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_POST_info_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_bus * video = seq - > private ;
2008-10-10 10:22:59 +04:00
unsigned long long options ;
2005-08-05 08:44:28 +04:00
int status ;
2005-04-17 02:20:36 +04:00
if ( ! video )
goto end ;
status = acpi_video_bus_POST_options ( video , & options ) ;
if ( ACPI_SUCCESS ( status ) ) {
if ( ! ( options & 1 ) ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_WARNING PREFIX
" The motherboard VGA device is not listed as a possible POST device. \n " ) ;
printk ( KERN_WARNING PREFIX
2007-02-20 18:38:40 +03:00
" This indicates a BIOS bug. Please contact the manufacturer. \n " ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-04 19:03:07 +03:00
printk ( KERN_WARNING " %llx \n " , options ) ;
2007-02-20 18:38:40 +03:00
seq_printf ( seq , " can POST: <integrated video> " ) ;
2005-04-17 02:20:36 +04:00
if ( options & 2 )
seq_printf ( seq , " <PCI video> " ) ;
if ( options & 4 )
seq_printf ( seq , " <AGP video> " ) ;
seq_putc ( seq , ' \n ' ) ;
} else
seq_printf ( seq , " <not supported> \n " ) ;
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_bus_POST_info_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
return single_open ( file , acpi_video_bus_POST_info_seq_show ,
PDE ( inode ) - > data ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_POST_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_bus * video = seq - > private ;
2005-08-05 08:44:28 +04:00
int status ;
2008-10-10 10:22:59 +04:00
unsigned long long id ;
2005-04-17 02:20:36 +04:00
if ( ! video )
goto end ;
2005-08-05 08:44:28 +04:00
status = acpi_video_bus_get_POST ( video , & id ) ;
2005-04-17 02:20:36 +04:00
if ( ! ACPI_SUCCESS ( status ) ) {
seq_printf ( seq , " <not supported> \n " ) ;
goto end ;
}
2007-02-20 18:38:40 +03:00
seq_printf ( seq , " device POSTed is <%s> \n " , device_decode [ id & 3 ] ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_DOS_seq_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_bus * video = seq - > private ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
seq_printf ( seq , " DOS setting: <%d> \n " , video - > dos_setting ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_POST_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
return single_open ( file , acpi_video_bus_POST_seq_show ,
PDE ( inode ) - > data ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_DOS_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
return single_open ( file , acpi_video_bus_DOS_seq_show , PDE ( inode ) - > data ) ;
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_video_bus_write_POST ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * data )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2006-10-01 02:28:50 +04:00
struct seq_file * m = file - > private_data ;
struct acpi_video_bus * video = m - > private ;
2005-08-05 08:44:28 +04:00
char str [ 12 ] = { 0 } ;
2008-10-10 10:22:59 +04:00
unsigned long long opt , options ;
2005-04-17 02:20:36 +04:00
if ( ! video | | count + 1 > sizeof str )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
status = acpi_video_bus_POST_options ( video , & options ) ;
if ( ! ACPI_SUCCESS ( status ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( str , buffer , count ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
str [ count ] = 0 ;
opt = strtoul ( str , NULL , 0 ) ;
if ( opt > 3 )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2007-02-20 18:38:40 +03:00
/* just in case an OEM 'forgot' the motherboard... */
2005-04-17 02:20:36 +04:00
options | = 1 ;
if ( options & ( 1ul < < opt ) ) {
2005-08-05 08:44:28 +04:00
status = acpi_video_bus_set_POST ( video , opt ) ;
2005-04-17 02:20:36 +04:00
if ( ! ACPI_SUCCESS ( status ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}
2006-06-27 08:41:40 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_video_bus_write_DOS ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * data )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2006-10-01 02:28:50 +04:00
struct seq_file * m = file - > private_data ;
struct acpi_video_bus * video = m - > private ;
2005-08-05 08:44:28 +04:00
char str [ 12 ] = { 0 } ;
unsigned long opt ;
2005-04-17 02:20:36 +04:00
if ( ! video | | count + 1 > sizeof str )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( str , buffer , count ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
str [ count ] = 0 ;
opt = strtoul ( str , NULL , 0 ) ;
if ( opt > 7 )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
status = acpi_video_bus_DOS ( video , opt & 0x3 , ( opt & 0x4 ) > > 2 ) ;
2005-04-17 02:20:36 +04:00
if ( ! ACPI_SUCCESS ( status ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_add_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2007-11-05 19:43:34 +03:00
struct acpi_video_bus * video = acpi_driver_data ( device ) ;
struct proc_dir_entry * device_dir ;
struct proc_dir_entry * entry ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:34 +03:00
device_dir = proc_mkdir ( acpi_device_bid ( device ) , acpi_video_dir ) ;
if ( ! device_dir )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
/* 'info' [R] */
2008-05-01 04:10:02 +04:00
entry = proc_create_data ( " info " , S_IRUGO , device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_bus_info_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_dir ;
2005-04-17 02:20:36 +04:00
/* 'ROM' [R] */
2008-05-01 04:10:02 +04:00
entry = proc_create_data ( " ROM " , S_IRUGO , device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_bus_ROM_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_info ;
2005-04-17 02:20:36 +04:00
/* 'POST_info' [R] */
2008-05-01 04:10:02 +04:00
entry = proc_create_data ( " POST_info " , S_IRUGO , device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_bus_POST_info_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_rom ;
2005-04-17 02:20:36 +04:00
/* 'POST' [R/W] */
2008-04-30 22:52:52 +04:00
entry = proc_create_data ( " POST " , S_IFREG | S_IRUGO | S_IWUSR ,
2008-05-01 04:10:02 +04:00
device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_bus_POST_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_post_info ;
2005-04-17 02:20:36 +04:00
/* 'DOS' [R/W] */
2008-04-30 22:52:52 +04:00
entry = proc_create_data ( " DOS " , S_IFREG | S_IRUGO | S_IWUSR ,
2008-05-01 04:10:02 +04:00
device_dir ,
2008-04-29 12:02:27 +04:00
& acpi_video_bus_DOS_fops ,
acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2007-11-05 19:43:34 +03:00
goto err_remove_post ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:34 +03:00
video - > dir = acpi_device_dir ( device ) = device_dir ;
2006-06-27 08:41:40 +04:00
return 0 ;
2007-11-05 19:43:34 +03:00
err_remove_post :
remove_proc_entry ( " POST " , device_dir ) ;
err_remove_post_info :
remove_proc_entry ( " POST_info " , device_dir ) ;
err_remove_rom :
remove_proc_entry ( " ROM " , device_dir ) ;
err_remove_info :
remove_proc_entry ( " info " , device_dir ) ;
err_remove_dir :
remove_proc_entry ( acpi_device_bid ( device ) , acpi_video_dir ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_remove_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2007-11-05 19:43:34 +03:00
struct proc_dir_entry * device_dir = acpi_device_dir ( device ) ;
if ( device_dir ) {
remove_proc_entry ( " info " , device_dir ) ;
remove_proc_entry ( " ROM " , device_dir ) ;
remove_proc_entry ( " POST_info " , device_dir ) ;
remove_proc_entry ( " POST " , device_dir ) ;
remove_proc_entry ( " DOS " , device_dir ) ;
2005-08-05 08:44:28 +04:00
remove_proc_entry ( acpi_device_bid ( device ) , acpi_video_dir ) ;
2005-04-17 02:20:36 +04:00
acpi_device_dir ( device ) = NULL ;
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Driver Interface
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* device interface */
2007-01-04 07:40:53 +03:00
static struct acpi_video_device_attrib *
acpi_video_get_device_attr ( struct acpi_video_bus * video , unsigned long device_id )
{
2007-11-05 19:43:33 +03:00
struct acpi_video_enumerated_device * ids ;
int i ;
for ( i = 0 ; i < video - > attached_count ; i + + ) {
ids = & video - > attached_array [ i ] ;
if ( ( ids - > value . int_val & 0xffff ) = = device_id )
return & ids - > value . attrib ;
}
2007-01-04 07:40:53 +03:00
return NULL ;
}
2005-04-17 02:20:36 +04:00
static int
2005-08-05 08:44:28 +04:00
acpi_video_bus_get_one_device ( struct acpi_device * device ,
struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
2008-10-10 10:22:59 +04:00
unsigned long long device_id ;
2006-04-27 13:25:00 +04:00
int status ;
2005-08-05 08:44:28 +04:00
struct acpi_video_device * data ;
2007-01-04 07:40:53 +03:00
struct acpi_video_device_attrib * attribute ;
2005-04-17 02:20:36 +04:00
if ( ! device | | ! video )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
status =
acpi_evaluate_integer ( device - > handle , " _ADR " , NULL , & device_id ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_SUCCESS ( status ) ) {
2006-12-19 23:56:11 +03:00
data = kzalloc ( sizeof ( struct acpi_video_device ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
strcpy ( acpi_device_name ( device ) , ACPI_VIDEO_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_VIDEO_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = data ;
2005-04-17 02:20:36 +04:00
data - > device_id = device_id ;
data - > video = video ;
data - > dev = device ;
2007-01-04 07:40:53 +03:00
attribute = acpi_video_get_device_attr ( video , device_id ) ;
if ( ( attribute ! = NULL ) & & attribute - > device_id_scheme ) {
switch ( attribute - > display_type ) {
case ACPI_VIDEO_DISPLAY_CRT :
data - > flags . crt = 1 ;
break ;
case ACPI_VIDEO_DISPLAY_TV :
data - > flags . tvout = 1 ;
break ;
case ACPI_VIDEO_DISPLAY_DVI :
data - > flags . dvi = 1 ;
break ;
case ACPI_VIDEO_DISPLAY_LCD :
data - > flags . lcd = 1 ;
break ;
default :
data - > flags . unknown = 1 ;
break ;
}
if ( attribute - > bios_can_detect )
data - > flags . bios = 1 ;
} else
2005-04-17 02:20:36 +04:00
data - > flags . unknown = 1 ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
acpi_video_device_bind ( video , data ) ;
acpi_video_device_find_cap ( data ) ;
2006-05-20 00:54:48 +04:00
status = acpi_install_notify_handler ( device - > handle ,
2005-08-05 08:44:28 +04:00
ACPI_DEVICE_NOTIFY ,
acpi_video_device_notify ,
data ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_ERR PREFIX
" Error installing notify handler \n " ) ;
2006-04-27 13:25:00 +04:00
if ( data - > brightness )
kfree ( data - > brightness - > levels ) ;
kfree ( data - > brightness ) ;
kfree ( data ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 19:43:32 +03:00
mutex_lock ( & video - > device_list_lock ) ;
2005-04-17 02:20:36 +04:00
list_add_tail ( & data - > entry , & video - > video_device_list ) ;
2007-11-05 19:43:32 +03:00
mutex_unlock ( & video - > device_list_lock ) ;
2005-04-17 02:20:36 +04:00
acpi_video_device_add_fs ( device ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-27 08:41:40 +04:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
}
/*
* Arg :
* video : video bus device
*
* Return :
* none
*
* Enumerate the video device list of the video bus ,
* bind the ids with the corresponding video devices
* under the video bus .
2005-08-05 08:44:28 +04:00
*/
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
static void acpi_video_device_rebind ( struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
2007-11-05 19:43:31 +03:00
struct acpi_video_device * dev ;
2007-11-05 19:43:32 +03:00
mutex_lock ( & video - > device_list_lock ) ;
2007-11-05 19:43:31 +03:00
list_for_each_entry ( dev , & video - > video_device_list , entry )
2005-08-05 08:44:28 +04:00
acpi_video_device_bind ( video , dev ) ;
2007-11-05 19:43:31 +03:00
2007-11-05 19:43:32 +03:00
mutex_unlock ( & video - > device_list_lock ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Arg :
* video : video bus device
* device : video output device under the video
* bus
*
* Return :
* none
*
* Bind the ids with the corresponding video devices
* under the video bus .
2005-08-05 08:44:28 +04:00
*/
2005-04-17 02:20:36 +04:00
static void
2005-08-05 08:44:28 +04:00
acpi_video_device_bind ( struct acpi_video_bus * video ,
struct acpi_video_device * device )
2005-04-17 02:20:36 +04:00
{
2007-11-05 19:43:33 +03:00
struct acpi_video_enumerated_device * ids ;
2005-08-05 08:44:28 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:33 +03:00
for ( i = 0 ; i < video - > attached_count ; i + + ) {
ids = & video - > attached_array [ i ] ;
if ( device - > device_id = = ( ids - > value . int_val & 0xffff ) ) {
ids - > bind_info = device ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " device_bind %d \n " , i ) ) ;
}
}
}
/*
* Arg :
* video : video bus device
*
* Return :
* < 0 : error
*
* Call _DOD to enumerate all devices attached to display adapter
*
2005-08-05 08:44:28 +04:00
*/
2005-04-17 02:20:36 +04:00
static int acpi_video_device_enumerate ( struct acpi_video_bus * video )
{
2005-08-05 08:44:28 +04:00
int status ;
int count ;
int i ;
2007-11-05 19:43:33 +03:00
struct acpi_video_enumerated_device * active_list ;
2005-08-05 08:44:28 +04:00
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * dod = NULL ;
union acpi_object * obj ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:48 +04:00
status = acpi_evaluate_object ( video - > device - > handle , " _DOD " , NULL , & buffer ) ;
2005-04-17 02:20:36 +04:00
if ( ! ACPI_SUCCESS ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Evaluating _DOD " ) ) ;
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
2006-10-01 02:28:50 +04:00
dod = buffer . pointer ;
2005-04-17 02:20:36 +04:00
if ( ! dod | | ( dod - > type ! = ACPI_TYPE_PACKAGE ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Invalid _DOD data " ) ) ;
2005-04-17 02:20:36 +04:00
status = - EFAULT ;
goto out ;
}
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Found %d video heads in _DOD \n " ,
2005-08-05 08:44:28 +04:00
dod - > package . count ) ) ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:33 +03:00
active_list = kcalloc ( 1 + dod - > package . count ,
sizeof ( struct acpi_video_enumerated_device ) ,
GFP_KERNEL ) ;
if ( ! active_list ) {
2005-04-17 02:20:36 +04:00
status = - ENOMEM ;
goto out ;
}
count = 0 ;
for ( i = 0 ; i < dod - > package . count ; i + + ) {
2006-10-01 02:28:50 +04:00
obj = & dod - > package . elements [ i ] ;
2005-04-17 02:20:36 +04:00
if ( obj - > type ! = ACPI_TYPE_INTEGER ) {
2007-11-05 19:43:33 +03:00
printk ( KERN_ERR PREFIX
" Invalid _DOD data in element %d \n " , i ) ;
continue ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 19:43:33 +03:00
active_list [ count ] . value . int_val = obj - > integer . value ;
active_list [ count ] . bind_info = NULL ;
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " dod element[%d] = %d \n " , i ,
( int ) obj - > integer . value ) ) ;
2005-04-17 02:20:36 +04:00
count + + ;
}
2005-11-07 12:01:32 +03:00
kfree ( video - > attached_array ) ;
2005-08-05 08:44:28 +04:00
2007-11-05 19:43:33 +03:00
video - > attached_array = active_list ;
2005-04-17 02:20:36 +04:00
video - > attached_count = count ;
2007-11-05 19:43:33 +03:00
out :
2006-06-30 11:19:10 +04:00
kfree ( buffer . pointer ) ;
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int
acpi_video_get_next_level ( struct acpi_video_device * device ,
u32 level_current , u32 event )
2005-04-17 02:20:36 +04:00
{
2007-09-03 16:30:08 +04:00
int min , max , min_above , max_below , i , l , delta = 255 ;
2006-12-19 23:56:14 +03:00
max = max_below = 0 ;
min = min_above = 255 ;
2007-09-03 16:30:08 +04:00
/* Find closest level to level_current */
2009-02-02 06:33:41 +03:00
for ( i = 2 ; i < device - > brightness - > count ; i + + ) {
2007-09-03 16:30:08 +04:00
l = device - > brightness - > levels [ i ] ;
if ( abs ( l - level_current ) < abs ( delta ) ) {
delta = l - level_current ;
if ( ! delta )
break ;
}
}
/* Ajust level_current to closest available level */
level_current + = delta ;
2009-02-02 06:33:41 +03:00
for ( i = 2 ; i < device - > brightness - > count ; i + + ) {
2006-12-19 23:56:14 +03:00
l = device - > brightness - > levels [ i ] ;
if ( l < min )
min = l ;
if ( l > max )
max = l ;
if ( l < min_above & & l > level_current )
min_above = l ;
if ( l > max_below & & l < level_current )
max_below = l ;
}
switch ( event ) {
case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS :
return ( level_current < max ) ? min_above : min ;
case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS :
return ( level_current < max ) ? min_above : max ;
case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS :
return ( level_current > min ) ? max_below : min ;
case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS :
case ACPI_VIDEO_NOTIFY_DISPLAY_OFF :
return 0 ;
default :
return level_current ;
}
2005-04-17 02:20:36 +04:00
}
2009-03-18 11:27:08 +03:00
static int
2005-08-05 08:44:28 +04:00
acpi_video_switch_brightness ( struct acpi_video_device * device , int event )
2005-04-17 02:20:36 +04:00
{
2008-10-10 10:22:59 +04:00
unsigned long long level_current , level_next ;
2009-03-18 11:27:08 +03:00
int result = - EINVAL ;
2008-06-24 01:50:42 +04:00
if ( ! device - > brightness )
2009-03-18 11:27:08 +03:00
goto out ;
result = acpi_video_device_lcd_get_level_current ( device ,
& level_current ) ;
if ( result )
goto out ;
2005-04-17 02:20:36 +04:00
level_next = acpi_video_get_next_level ( device , level_current , event ) ;
2009-03-18 11:27:08 +03:00
2009-03-18 11:27:10 +03:00
result = acpi_video_device_lcd_set_level ( device , level_next ) ;
2009-03-18 11:27:08 +03:00
out :
if ( result )
printk ( KERN_ERR PREFIX " Failed to switch the brightness \n " ) ;
return result ;
2005-04-17 02:20:36 +04:00
}
static int
2005-08-05 08:44:28 +04:00
acpi_video_bus_get_devices ( struct acpi_video_bus * video ,
struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status = 0 ;
2007-11-05 19:43:31 +03:00
struct acpi_device * dev ;
2005-04-17 02:20:36 +04:00
acpi_video_device_enumerate ( video ) ;
2007-11-05 19:43:31 +03:00
list_for_each_entry ( dev , & device - > children , node ) {
2005-04-17 02:20:36 +04:00
status = acpi_video_bus_get_one_device ( dev , video ) ;
if ( ACPI_FAILURE ( status ) ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_WARNING PREFIX
" Cant attach device " ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
}
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_put_one_device ( struct acpi_video_device * device )
2005-04-17 02:20:36 +04:00
{
2005-07-30 12:18:00 +04:00
acpi_status status ;
2005-04-17 02:20:36 +04:00
struct acpi_video_bus * video ;
if ( ! device | | ! device - > video )
2006-06-27 08:41:40 +04:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
video = device - > video ;
acpi_video_device_remove_fs ( device - > dev ) ;
2006-05-20 00:54:48 +04:00
status = acpi_remove_notify_handler ( device - > dev - > handle ,
2005-08-05 08:44:28 +04:00
ACPI_DEVICE_NOTIFY ,
acpi_video_device_notify ) ;
2009-06-22 07:31:14 +04:00
sysfs_remove_link ( & device - > backlight - > dev . kobj , " device " ) ;
2007-02-11 02:07:48 +03:00
backlight_device_unregister ( device - > backlight ) ;
2009-08-30 07:03:16 +04:00
if ( device - > cooling_dev ) {
2008-01-17 10:51:22 +03:00
sysfs_remove_link ( & device - > dev - > dev . kobj ,
" thermal_cooling " ) ;
2009-08-30 07:03:16 +04:00
sysfs_remove_link ( & device - > cooling_dev - > device . kobj ,
2008-01-17 10:51:22 +03:00
" device " ) ;
2009-08-30 07:03:16 +04:00
thermal_cooling_device_unregister ( device - > cooling_dev ) ;
device - > cooling_dev = NULL ;
2008-01-17 10:51:22 +03:00
}
2007-05-09 17:07:05 +04:00
video_output_unregister ( device - > output_dev ) ;
2007-11-05 19:43:31 +03:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_put_devices ( struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int status ;
2007-11-05 19:43:31 +03:00
struct acpi_video_device * dev , * next ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:32 +03:00
mutex_lock ( & video - > device_list_lock ) ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:31 +03:00
list_for_each_entry_safe ( dev , next , & video - > video_device_list , entry ) {
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:31 +03:00
status = acpi_video_bus_put_one_device ( dev ) ;
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) )
printk ( KERN_WARNING PREFIX
" hhuuhhuu bug in acpi video driver. \n " ) ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:31 +03:00
if ( dev - > brightness ) {
kfree ( dev - > brightness - > levels ) ;
kfree ( dev - > brightness ) ;
}
list_del ( & dev - > entry ) ;
kfree ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 19:43:32 +03:00
mutex_unlock ( & video - > device_list_lock ) ;
2007-11-05 19:43:31 +03:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* acpi_video interface */
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_start_devices ( struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
2007-09-14 07:46:22 +04:00
return acpi_video_bus_DOS ( video , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_stop_devices ( struct acpi_video_bus * video )
2005-04-17 02:20:36 +04:00
{
return acpi_video_bus_DOS ( video , 0 , 1 ) ;
}
2009-04-07 19:37:11 +04:00
static void acpi_video_bus_notify ( struct acpi_device * device , u32 event )
2005-04-17 02:20:36 +04:00
{
2009-04-07 19:37:11 +04:00
struct acpi_video_bus * video = acpi_driver_data ( device ) ;
2007-08-20 14:23:53 +04:00
struct input_dev * input ;
int keycode ;
2005-04-17 02:20:36 +04:00
if ( ! video )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
2007-08-20 14:23:53 +04:00
input = video - > input ;
2005-04-17 02:20:36 +04:00
switch ( event ) {
2007-02-20 18:38:40 +03:00
case ACPI_VIDEO_NOTIFY_SWITCH : /* User requested a switch,
2005-04-17 02:20:36 +04:00
* most likely via hotkey . */
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_SWITCHVIDEOMODE ;
2005-04-17 02:20:36 +04:00
break ;
2007-02-20 18:38:40 +03:00
case ACPI_VIDEO_NOTIFY_PROBE : /* User plugged in or removed a video
2005-04-17 02:20:36 +04:00
* connector . */
acpi_video_device_enumerate ( video ) ;
acpi_video_device_rebind ( video ) ;
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_SWITCHVIDEOMODE ;
2005-04-17 02:20:36 +04:00
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_CYCLE : /* Cycle Display output hotkey pressed. */
2007-08-25 09:44:01 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_SWITCHVIDEOMODE ;
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT : /* Next Display output hotkey pressed. */
2007-08-25 09:44:01 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_VIDEO_NEXT ;
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_PREV_OUTPUT : /* previous Display output hotkey pressed. */
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_VIDEO_PREV ;
2005-04-17 02:20:36 +04:00
break ;
default :
2007-08-20 14:23:53 +04:00
keycode = KEY_UNKNOWN ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" Unsupported event [0x%x] \n " , event ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2008-01-25 09:48:12 +03:00
acpi_notifier_call_chain ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
input_report_key ( input , keycode , 1 ) ;
input_sync ( input ) ;
input_report_key ( input , keycode , 0 ) ;
input_sync ( input ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static void acpi_video_device_notify ( acpi_handle handle , u32 event , void * data )
2005-04-17 02:20:36 +04:00
{
2006-10-01 02:28:50 +04:00
struct acpi_video_device * video_device = data ;
2005-08-05 08:44:28 +04:00
struct acpi_device * device = NULL ;
2007-08-20 14:23:53 +04:00
struct acpi_video_bus * bus ;
struct input_dev * input ;
int keycode ;
2005-04-17 02:20:36 +04:00
if ( ! video_device )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:40 +04:00
device = video_device - > dev ;
2007-08-20 14:23:53 +04:00
bus = video_device - > video ;
input = bus - > input ;
2005-04-17 02:20:36 +04:00
switch ( event ) {
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS : /* Cycle brightness */
2008-01-25 09:47:49 +03:00
if ( brightness_switch_enabled )
acpi_video_switch_brightness ( video_device , event ) ;
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_BRIGHTNESS_CYCLE ;
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS : /* Increase brightness */
2008-01-25 09:47:49 +03:00
if ( brightness_switch_enabled )
acpi_video_switch_brightness ( video_device , event ) ;
2007-08-25 09:44:01 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_BRIGHTNESSUP ;
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS : /* Decrease brightness */
2008-01-25 09:47:49 +03:00
if ( brightness_switch_enabled )
acpi_video_switch_brightness ( video_device , event ) ;
2007-08-25 09:44:01 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_BRIGHTNESSDOWN ;
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS : /* zero brightnesss */
2008-01-25 09:47:49 +03:00
if ( brightness_switch_enabled )
acpi_video_switch_brightness ( video_device , event ) ;
2007-08-25 09:44:01 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_BRIGHTNESS_ZERO ;
break ;
2005-08-05 08:44:28 +04:00
case ACPI_VIDEO_NOTIFY_DISPLAY_OFF : /* display device off */
2008-01-25 09:47:49 +03:00
if ( brightness_switch_enabled )
acpi_video_switch_brightness ( video_device , event ) ;
2007-08-23 23:20:26 +04:00
acpi_bus_generate_proc_event ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
keycode = KEY_DISPLAY_OFF ;
2005-04-17 02:20:36 +04:00
break ;
default :
2007-08-20 14:23:53 +04:00
keycode = KEY_UNKNOWN ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" Unsupported event [0x%x] \n " , event ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2007-08-20 14:23:53 +04:00
2008-01-25 09:48:12 +03:00
acpi_notifier_call_chain ( device , event , 0 ) ;
2007-08-20 14:23:53 +04:00
input_report_key ( input , keycode , 1 ) ;
input_sync ( input ) ;
input_report_key ( input , keycode , 0 ) ;
input_sync ( input ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2007-08-25 10:23:31 +04:00
static int instance ;
2008-02-05 10:31:24 +03:00
static int acpi_video_resume ( struct acpi_device * device )
{
struct acpi_video_bus * video ;
struct acpi_video_device * video_device ;
int i ;
if ( ! device | | ! acpi_driver_data ( device ) )
return - EINVAL ;
video = acpi_driver_data ( device ) ;
for ( i = 0 ; i < video - > attached_count ; i + + ) {
video_device = video - > attached_array [ i ] . bind_info ;
if ( video_device & & video_device - > backlight )
acpi_video_set_brightness ( video_device - > backlight ) ;
}
return AE_OK ;
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2007-11-05 19:43:30 +03:00
struct acpi_video_bus * video ;
2007-08-20 14:23:53 +04:00
struct input_dev * input ;
2007-11-05 19:43:30 +03:00
int error ;
2005-04-17 02:20:36 +04:00
2006-12-19 23:56:11 +03:00
video = kzalloc ( sizeof ( struct acpi_video_bus ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! video )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-08-25 10:23:31 +04:00
/* a hack to fix the duplicate name "VID" problem on T61 */
if ( ! strcmp ( device - > pnp . bus_id , " VID " ) ) {
if ( instance )
device - > pnp . bus_id [ 3 ] = ' 0 ' + instance ;
instance + + ;
}
2009-02-03 06:55:01 +03:00
/* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
if ( ! strcmp ( device - > pnp . bus_id , " VGA " ) ) {
if ( instance )
device - > pnp . bus_id [ 3 ] = ' 0 ' + instance ;
instance + + ;
}
2007-08-25 10:23:31 +04:00
2006-05-20 00:54:40 +04:00
video - > device = device ;
2005-04-17 02:20:36 +04:00
strcpy ( acpi_device_name ( device ) , ACPI_VIDEO_BUS_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_VIDEO_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = video ;
2005-04-17 02:20:36 +04:00
acpi_video_bus_find_cap ( video ) ;
2007-11-05 19:43:30 +03:00
error = acpi_video_bus_check ( video ) ;
if ( error )
goto err_free_video ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:30 +03:00
error = acpi_video_bus_add_fs ( device ) ;
if ( error )
goto err_free_video ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:32 +03:00
mutex_init ( & video - > device_list_lock ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & video - > video_device_list ) ;
acpi_video_bus_get_devices ( video , device ) ;
acpi_video_bus_start_devices ( video ) ;
2007-08-20 14:23:53 +04:00
video - > input = input = input_allocate_device ( ) ;
2007-11-05 19:43:30 +03:00
if ( ! input ) {
error = - ENOMEM ;
2009-04-07 19:37:11 +04:00
goto err_stop_video ;
2007-11-05 19:43:30 +03:00
}
2007-08-20 14:23:53 +04:00
snprintf ( video - > phys , sizeof ( video - > phys ) ,
" %s/video/input0 " , acpi_device_hid ( video - > device ) ) ;
input - > name = acpi_device_name ( video - > device ) ;
input - > phys = video - > phys ;
input - > id . bustype = BUS_HOST ;
input - > id . product = 0x06 ;
2007-11-05 19:43:29 +03:00
input - > dev . parent = & device - > dev ;
2007-08-20 14:23:53 +04:00
input - > evbit [ 0 ] = BIT ( EV_KEY ) ;
set_bit ( KEY_SWITCHVIDEOMODE , input - > keybit ) ;
set_bit ( KEY_VIDEO_NEXT , input - > keybit ) ;
set_bit ( KEY_VIDEO_PREV , input - > keybit ) ;
set_bit ( KEY_BRIGHTNESS_CYCLE , input - > keybit ) ;
set_bit ( KEY_BRIGHTNESSUP , input - > keybit ) ;
set_bit ( KEY_BRIGHTNESSDOWN , input - > keybit ) ;
set_bit ( KEY_BRIGHTNESS_ZERO , input - > keybit ) ;
set_bit ( KEY_DISPLAY_OFF , input - > keybit ) ;
set_bit ( KEY_UNKNOWN , input - > keybit ) ;
2007-11-05 19:43:30 +03:00
error = input_register_device ( input ) ;
if ( error )
goto err_free_input_dev ;
2007-08-20 14:23:53 +04:00
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO PREFIX " %s [%s] (multi-head: %s rom: %s post: %s) \n " ,
2005-08-05 08:44:28 +04:00
ACPI_VIDEO_DEVICE_NAME , acpi_device_bid ( device ) ,
video - > flags . multihead ? " yes " : " no " ,
video - > flags . rom ? " yes " : " no " ,
video - > flags . post ? " yes " : " no " ) ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:30 +03:00
return 0 ;
err_free_input_dev :
input_free_device ( input ) ;
err_stop_video :
acpi_video_bus_stop_devices ( video ) ;
acpi_video_bus_put_devices ( video ) ;
kfree ( video - > attached_array ) ;
acpi_video_bus_remove_fs ( device ) ;
err_free_video :
kfree ( video ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = NULL ;
2005-04-17 02:20:36 +04:00
2007-11-05 19:43:30 +03:00
return error ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_video_bus_remove ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_video_bus * video = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! device | | ! acpi_driver_data ( device ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
video = acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
acpi_video_bus_stop_devices ( video ) ;
acpi_video_bus_put_devices ( video ) ;
acpi_video_bus_remove_fs ( device ) ;
2007-08-20 14:23:53 +04:00
input_unregister_device ( video - > input ) ;
2005-11-07 12:01:32 +03:00
kfree ( video - > attached_array ) ;
2005-04-17 02:20:36 +04:00
kfree ( video ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-20 00:35:39 +03:00
static int __init intel_opregion_present ( void )
{
# if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
struct pci_dev * dev = NULL ;
u32 address ;
for_each_pci_dev ( dev ) {
if ( ( dev - > class > > 8 ) ! = PCI_CLASS_DISPLAY_VGA )
continue ;
if ( dev - > vendor ! = PCI_VENDOR_ID_INTEL )
continue ;
pci_read_config_dword ( dev , 0xfc , & address ) ;
if ( ! address )
continue ;
return 1 ;
}
# endif
return 0 ;
}
int acpi_video_register ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
2009-06-16 07:23:13 +04:00
if ( register_count ) {
/*
* if the function of acpi_video_register is already called ,
* don ' t register the acpi_vide_bus again and return no error .
*/
return 0 ;
}
2005-04-17 02:20:36 +04:00
acpi_video_dir = proc_mkdir ( ACPI_VIDEO_CLASS , acpi_root_dir ) ;
if ( ! acpi_video_dir )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_register_driver ( & acpi_video_bus ) ;
if ( result < 0 ) {
remove_proc_entry ( ACPI_VIDEO_CLASS , acpi_root_dir ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2009-06-16 07:23:13 +04:00
/*
* When the acpi_video_bus is loaded successfully , increase
* the counter reference .
*/
register_count = 1 ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-20 00:35:39 +03:00
EXPORT_SYMBOL ( acpi_video_register ) ;
2009-06-16 07:23:13 +04:00
void acpi_video_unregister ( void )
{
if ( ! register_count ) {
/*
* If the acpi video bus is already unloaded , don ' t
* unload it again and return directly .
*/
return ;
}
acpi_bus_unregister_driver ( & acpi_video_bus ) ;
remove_proc_entry ( ACPI_VIDEO_CLASS , acpi_root_dir ) ;
register_count = 0 ;
return ;
}
EXPORT_SYMBOL ( acpi_video_unregister ) ;
2009-03-20 00:35:39 +03:00
/*
* This is kind of nasty . Hardware using Intel chipsets may require
* the video opregion code to be run first in order to initialise
* state before any ACPI video calls are made . To handle this we defer
* registration of the video class until the opregion code has run .
*/
static int __init acpi_video_init ( void )
{
2009-04-24 20:13:18 +04:00
dmi_check_system ( video_dmi_table ) ;
2009-03-20 00:35:39 +03:00
if ( intel_opregion_present ( ) )
return 0 ;
return acpi_video_register ( ) ;
}
2005-04-17 02:20:36 +04:00
2009-06-16 07:23:13 +04:00
static void __exit acpi_video_exit ( void )
2005-04-17 02:20:36 +04:00
{
2009-06-16 07:23:13 +04:00
acpi_video_unregister ( ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
module_init ( acpi_video_init ) ;
module_exit ( acpi_video_exit ) ;