Re: [PATCH 02/10] Check for ACPI backlight support otherwise use vendor ACPI drivers

Zhang Rui
2008-08-04T01:58:47Z

On Fri, 2008-08-01 at 23:37 +0800, Thomas Renninger wrote:
> If an ACPI graphics device supports backlight brightness functions
> (cmp. with
> latest ACPI spec Appendix B), let the ACPI video driver control
> backlight and
> switch backlight control off in vendor specific ACPI drivers
> (asus_acpi,
> thinkpad_acpi, eeepc, fujitsu_laptop, msi_laptop, sony_laptop,
> acer-wmi).
>
> Currently it is possible to load above drivers and let both poke on
> the
> brightness HW registers, the video and vendor specific ACPI drivers ->
> bad.
>
> This patch provides the basic support to check for BIOS capabilities
> before
> driver loading time. Driver specific modifications are in separate
> follow up
> patches.
>
> acpi_backlight=vendor/video
> boot params forces video.ko or vendor specific drivers to keep
> its
> fingers off backlight control even it would find needed
> functions.
> The corresponding vendor specific driver be used then.
>
> Signed-off-by: Thomas Renninger <trenn@suse.de>
Acked-by: Zhang Rui <rui.zhang@intel.com>

> ---
> Documentation/kernel-parameters.txt | 13 ++
> drivers/acpi/Makefile | 5 +
> drivers/acpi/scan.c | 32 +----
> drivers/acpi/video.c | 28 ++--
> drivers/acpi/video_detect.c | 268
> +++++++++++++++++++++++++++++++++++
> include/linux/acpi.h | 44 ++++++
> 6 files changed, 347 insertions(+), 43 deletions(-)
> create mode 100644 drivers/acpi/video_detect.c
>
> diff --git a/Documentation/kernel-parameters.txt
> b/Documentation/kernel-parameters.txt
> index 09ad745..0fea7c2 100644
> --- a/Documentation/kernel-parameters.txt
> +++ b/Documentation/kernel-parameters.txt
> @@ -194,6 +194,19 @@ and is between 256 and 4096 characters. It is
> defined in the file
> that require a timer override, but don't have
> HPET
>
> + acpi_backlight= [HW,ACPI]
> + acpi_backlight=vendor
> + acpi_backlight=video
> + If set to vendor, it enforces the use of a
> + vendor specific ACPI driver for backlight
> switching
> + (e.g. thinkpad_acpi, sony_acpi, etc.) instead
> + of the video.ko driver.
> +
> + acpi_display_output= [HW,ACPI]
> + acpi_display_output=vendor
> + acpi_display_output=video
> + See above.
> +
> acpi.debug_layer= [HW,ACPI]
> Format: <int>
> Each bit of the <int> indicates an ACPI debug
> layer,
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 52a4cd4..6b7ad0f 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -46,7 +46,12 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o
> obj-$(CONFIG_ACPI_FAN) += fan.o
> obj-$(CONFIG_ACPI_DOCK) += dock.o
> obj-$(CONFIG_ACPI_BAY) += bay.o
> +
> obj-$(CONFIG_ACPI_VIDEO) += video.o
> +ifdef CONFIG_ACPI_VIDEO
> +obj-y += video_detect.o
> +endif
> +
> obj-y += pci_root.o pci_link.o pci_irq.o
> pci_bind.o
> obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
> obj-$(CONFIG_ACPI_POWER) += power.o
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index f3132aa..2dcb953 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -901,36 +901,6 @@ static void acpi_device_get_busid(struct
> acpi_device *device,
> }
> }
>
> -static int
> -acpi_video_bus_match(struct acpi_device *device)
> -{
> - acpi_handle h_dummy;
> -
> - if (!device)
> - return -EINVAL;
> -
> - /* Since there is no HID, CID for ACPI Video drivers, we have
> - * to check well known required nodes for each feature we
> support.
> - */
> -
> - /* Does this device able to support video switching ? */
> - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD",
> &h_dummy)) &&
> - ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS",
> &h_dummy)))
> - return 0;
> -
> - /* Does this device able to retrieve a video ROM ? */
> - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM",
> &h_dummy)))
> - return 0;
> -
> - /* Does this device able to configure which video head to be
> POSTed ? */
> - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO",
> &h_dummy)) &&
> - ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD",
> &h_dummy)) &&
> - ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD",
> &h_dummy)))
> - return 0;
> -
> - return -ENODEV;
> -}
> -
> /*
> * acpi_bay_match - see if a device is an ejectable driver bay
> *
> @@ -1013,7 +983,7 @@ static void acpi_device_set_id(struct acpi_device
> *device,
> will get autoloaded and the device might still
> match
> against another driver.
> */
> - if (ACPI_SUCCESS(acpi_video_bus_match(device)))
> + if (acpi_is_video_device(device))
> cid_add = ACPI_VIDEO_HID;
> else if (ACPI_SUCCESS(acpi_bay_match(device)))
> cid_add = ACPI_BAY_HID;
> diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
> index 649888a..7abd326 100644
> --- a/drivers/acpi/video.c
> +++ b/drivers/acpi/video.c
> @@ -739,7 +739,8 @@ static void acpi_video_device_find_cap(struct
> acpi_video_device *device)
> device->cap._DSS = 1;
> }
>
> - max_level = acpi_video_init_brightness(device);
> + if (acpi_video_backlight_support())
> + max_level = acpi_video_init_brightness(device);
>
> if (device->cap._BCL && device->cap._BCM && max_level > 0) {
> int result;
> @@ -786,18 +787,21 @@ static void acpi_video_device_find_cap(struct
> acpi_video_device *device)
> printk(KERN_ERR PREFIX "Create sysfs link\n");
>
> }
> - if (device->cap._DCS && device->cap._DSS){
> - static int count = 0;
> - 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);
> +
> + 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);
> + }
> }
> - return;
> }
>
> /*
> diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
> new file mode 100644
> index 0000000..85c3d2c
> --- /dev/null
> +++ b/drivers/acpi/video_detect.c
> @@ -0,0 +1,268 @@
> +/*
> + * Copyright (C) 2008 SuSE Linux Products GmbH
> + * Thomas Renninger <trenn@suse.de>
> + *
> + * May be copied or modified under the terms of the GNU General
> Public License
> + *
> + * video_detect.c:
> + * Provides acpi_is_video_device() for early scanning of ACPI devices
> in scan.c
> + * There a Linux specific (Spec does not provide a HID for video
> devices) is
> + * assinged
> + *
> + * After PCI devices are glued with ACPI devices
> + * acpi_get_physical_pci_device() can be called to identify ACPI
> graphics
> + * devices for which a real graphics card is plugged in
> + *
> + * Now acpi_video_get_capabilities() can be called to check which
> + * capabilities the graphics cards plugged in support. The check for
> general
> + * video capabilities will be triggered by the first caller of
> + * acpi_video_get_capabilities(NULL); which will happen when the
> first
> + * backlight (or display output) switching supporting driver calls:
> + * acpi_video_backlight_support();
> + *
> + * Depending on whether ACPI graphics extensions (cmp. ACPI spec
> Appendix B)
> + * are available, video.ko should be used to handle the device.
> + *
> + * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi,
> + * sony_acpi,... can take care about backlight brightness and display
> output
> + * switching.
> + *
> + * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a
> module (m)
> + * this file will not be compiled, acpi_video_get_capabilities() and
> + * acpi_video_backlight_support() will always return 0 and vendor
> specific
> + * drivers always can handle backlight.
> + *
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/dmi.h>
> +
> +ACPI_MODULE_NAME("video");
> +#define ACPI_VIDEO_COMPONENT 0x08000000
> +#define _COMPONENT ACPI_VIDEO_COMPONENT
> +
> +static long acpi_video_support;
> +static bool acpi_video_caps_checked;
> +
> +static acpi_status
> +acpi_backlight_cap_match(acpi_handle handle, u32 level, void
> *context,
> + void **retyurn_value)
> +{
> + long *cap = context;
> + acpi_handle h_dummy;
> +
> + if (ACPI_SUCCESS(acpi_get_handle(handle, "_BCM", &h_dummy)) &&
> + ACPI_SUCCESS(acpi_get_handle(handle, "_BCL", &h_dummy))) {
> + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic
> backlight "
> + "support\n"));
> + *cap |= ACPI_VIDEO_BACKLIGHT;
> + /* We have backlight support, no need to scan further
> */
> + return AE_CTRL_TERMINATE;
> + }
> + return 0;
> +}
> +
> +/* Returns true if the device is a video device which can be handled
> by
> + * video.ko.
> + * The device will get a Linux specific CID added in scan.c to
> + * identify the device as an ACPI graphics device
> + * Be aware that the graphics device may not be physically present
> + * Use acpi_video_get_capabilities() to detect general ACPI video
> + * capabilities of present cards
> + */
> +long acpi_is_video_device(struct acpi_device *device)
> +{
> + acpi_handle h_dummy;
> + long video_caps = 0;
> +
> + if (!device)
> + return 0;
> +
> + /* Does this device able to support video switching ? */
> + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD",
> &h_dummy)) &&
> + ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS",
> &h_dummy)))
> + video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
> +
> + /* Does this device able to retrieve a video ROM ? */
> + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM",
> &h_dummy)))
> + video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
> +
> + /* Does this device able to configure which video head to be
> POSTed ? */
> + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO",
> &h_dummy)) &&
> + ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD",
> &h_dummy)) &&
> + ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD",
> &h_dummy)))
> + video_caps |= ACPI_VIDEO_DEVICE_POSTING;
> +
> + /* Only check for backlight functionality if one of the above
> hit. */
> + if (video_caps)
> + acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle,
> + ACPI_UINT32_MAX,
> acpi_backlight_cap_match,
> + &video_caps, NULL);
> +
> + return video_caps;
> +}
> +EXPORT_SYMBOL(acpi_is_video_device);
> +
> +static acpi_status
> +find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
> +{
> + long *cap = context;
> + struct device *dev;
> + struct acpi_device *acpi_dev;
> +
> + const struct acpi_device_id video_ids[] = {
> + {ACPI_VIDEO_HID, 0},
> + {"", 0},
> + };
> + if (acpi_bus_get_device(handle, &acpi_dev))
> + return AE_OK;
> +
> + if (!acpi_match_device_ids(acpi_dev, video_ids)) {
> + dev = acpi_get_physical_pci_device(handle);
> + if (!dev)
> + return AE_OK;
> + put_device(dev);
> + *cap |= acpi_is_video_device(acpi_dev);
> + }
> + return AE_OK;
> +}
> +
> +/*
> + * Returns the video capabilities of a specific ACPI graphics device
> + *
> + * if NULL is passed as argument all ACPI devices are enumerated and
> + * all graphics capabilities of physically present devices are
> + * summerized and returned. This is cached and done only once.
> + */
> +long acpi_video_get_capabilities(acpi_handle graphics_handle)
> +{
> + long caps = 0;
> + struct acpi_device *tmp_dev;
> + acpi_status status;
> +
> + if (acpi_video_caps_checked && graphics_handle == NULL)
> + return acpi_video_support;
> +
> + if (!graphics_handle) {
> + /* Only do the global walk through all graphics
> devices once */
> + acpi_walk_namespace(ACPI_TYPE_DEVICE,
> ACPI_ROOT_OBJECT,
> + ACPI_UINT32_MAX, find_video,
> + &caps, NULL);
> + /* There might be boot param flags set already... */
> + acpi_video_support |= caps;
> + acpi_video_caps_checked = 1;
> + /* Add blacklists here. Be careful to use the right
> *DMI* bits
> + * to still be able to override logic via boot params,
> e.g.:
> + *
> + * if (dmi_name_in_vendors("XY")) {
> + * acpi_video_support |=
> + *
> ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR;
> + * acpi_video_support |=
> + * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
> + *}
> + */
> + } else {
> + status = acpi_bus_get_device(graphics_handle,
> &tmp_dev);
> + if (ACPI_FAILURE(status)) {
> + ACPI_EXCEPTION((AE_INFO, status, "Invalid
> device"));
> + return 0;
> + }
> + acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle,
> + ACPI_UINT32_MAX, find_video,
> + &caps, NULL);
> + }
> + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %
> s %s\n",
> + graphics_handle ? caps : acpi_video_support,
> + graphics_handle ? "on device " : "in
> general",
> + graphics_handle ? acpi_device_bid(tmp_dev) :
> ""));
> + return caps;
> +}
> +EXPORT_SYMBOL(acpi_video_get_capabilities);
> +
> +/* Returns true if video.ko can do backlight switching */
> +int acpi_video_backlight_support(void)
> +{
> + /*
> + * We must check whether the ACPI graphics device is
> physically plugged
> + * in. Therefore this must be called after binding PCI and
> ACPI devices
> + */
> + if (!acpi_video_caps_checked)
> + acpi_video_get_capabilities(NULL);
> +
> + /* First check for boot param -> highest prio */
> + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR)
> + return 0;
> + else if (acpi_video_support &
> ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO)
> + return 1;
> +
> + /* Then check for DMI blacklist -> second highest prio */
> + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR)
> + return 0;
> + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO)
> + return 1;
> +
> + /* Then go the default way */
> + return acpi_video_support & ACPI_VIDEO_BACKLIGHT;
> +}
> +EXPORT_SYMBOL(acpi_video_backlight_support);
> +
> +/*
> + * Returns true if video.ko can do display output switching.
> + * This does not work well/at all with binary graphics drivers
> + * which disable system io ranges and do it on their own.
> + */
> +int acpi_video_display_switch_support(void)
> +{
> + if (!acpi_video_caps_checked)
> + acpi_video_get_capabilities(NULL);
> +
> + if (acpi_video_support &
> ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR)
> + return 0;
> + else if (acpi_video_support &
> ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO)
> + return 1;
> +
> + if (acpi_video_support &
> ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR)
> + return 0;
> + else if (acpi_video_support &
> ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO)
> + return 1;
> +
> + return acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING;
> +}
> +EXPORT_SYMBOL(acpi_video_display_switch_support);
> +
> +/*
> + * Use acpi_display_output=vendor/video or
> acpi_backlight=vendor/video
> + * To force that backlight or display output switching is processed
> by vendor
> + * specific acpi drivers or video.ko driver.
> + */
> +int __init acpi_backlight(char *str)
> +{
> + if (str == NULL || *str == '\0')
> + return 1;
> + else {
> + if (!strcmp("vendor", str))
> + acpi_video_support |=
> + ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR;
> + if (!strcmp("video", str))
> + acpi_video_support |=
> +
> ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO;
> + }
> + return 1;
> +}
> +__setup("acpi_backlight=", acpi_backlight);
> +
> +int __init acpi_display_output(char *str)
> +{
> + if (str == NULL || *str == '\0')
> + return 1;
> + else {
> + if (!strcmp("vendor", str))
> + acpi_video_support |=
> +
> ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR;
> + if (!strcmp("video", str))
> + acpi_video_support |=
> +
> ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO;
> + }
> + return 1;
> +}
> +__setup("acpi_display_output=", acpi_display_output);
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index a171776..eb51726 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -202,6 +202,50 @@ extern bool wmi_has_guid(const char *guid);
>
> #endif /* CONFIG_ACPI_WMI */
>
> +#define ACPI_VIDEO_OUTPUT_SWITCHING 0x0001
> +#define ACPI_VIDEO_DEVICE_POSTING 0x0002
> +#define ACPI_VIDEO_ROM_AVAILABLE 0x0004
> +#define ACPI_VIDEO_BACKLIGHT 0x0008
> +#define ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR 0x0010
> +#define ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO 0x0020
> +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR 0x0040
> +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO 0x0080
> +#define ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 0x0100
> +#define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200
> +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400
> +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800
> +
> +#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
> +
> +extern long acpi_video_get_capabilities(acpi_handle
> graphics_dev_handle);
> +extern long acpi_is_video_device(struct acpi_device *device);
> +extern int acpi_video_backlight_support(void);
> +extern int acpi_video_display_switch_support(void);
> +
> +#else
> +
> +static inline long acpi_video_get_capabilities(acpi_handle
> graphics_dev_handle)
> +{
> + return 0;
> +}
> +
> +static inline long acpi_is_video_device(struct acpi_device *device)
> +{
> + return 0;
> +}
> +
> +static inline int acpi_video_backlight_support(void)
> +{
> + return 0;
> +}
> +
> +static inline int acpi_video_display_switch_support(void)
> +{
> + return 0;
> +}
> +
> +#endif /* defined(CONFIG_ACPI_VIDEO) ||
> defined(CONFIG_ACPI_VIDEO_MODULE) */
> +
> extern int acpi_blacklisted(void);
> #ifdef CONFIG_DMI
> extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id
> *d);
> --
> 1.5.4.5
>
>
>