Commit a4ecbb8a authored by Ike Panhc's avatar Ike Panhc Committed by Matthew Garrett

ideapad: add backlight driver

When acpi_backlight=vendor in cmdline or no backlight support in acpi video
device, ideapad-laptop will register backlight device and control brightness
and backlight power via the command in VPC2004.
Signed-off-by: default avatarIke Panhc <ike.pan@canonical.com>
Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
parent a84511f7
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#define IDEAPAD_RFKILL_DEV_NUM (3) #define IDEAPAD_RFKILL_DEV_NUM (3)
...@@ -44,6 +46,7 @@ struct ideapad_private { ...@@ -44,6 +46,7 @@ struct ideapad_private {
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device; struct platform_device *platform_device;
struct input_dev *inputdev; struct input_dev *inputdev;
struct backlight_device *blightdev;
unsigned long cfg; unsigned long cfg;
}; };
...@@ -309,8 +312,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, ...@@ -309,8 +312,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
return 0; return 0;
} }
static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
int dev)
{ {
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
...@@ -417,6 +419,98 @@ static void ideapad_input_report(struct ideapad_private *priv, ...@@ -417,6 +419,98 @@ static void ideapad_input_report(struct ideapad_private *priv,
sparse_keymap_report_event(priv->inputdev, scancode, 1, true); sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
} }
/*
* backlight
*/
static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
{
unsigned long now;
if (read_ec_data(ideapad_handle, 0x12, &now))
return -EIO;
return now;
}
static int ideapad_backlight_update_status(struct backlight_device *blightdev)
{
if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
return -EIO;
if (write_ec_cmd(ideapad_handle, 0x33,
blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
return -EIO;
return 0;
}
static const struct backlight_ops ideapad_backlight_ops = {
.get_brightness = ideapad_backlight_get_brightness,
.update_status = ideapad_backlight_update_status,
};
static int ideapad_backlight_init(struct ideapad_private *priv)
{
struct backlight_device *blightdev;
struct backlight_properties props;
unsigned long max, now, power;
if (read_ec_data(ideapad_handle, 0x11, &max))
return -EIO;
if (read_ec_data(ideapad_handle, 0x12, &now))
return -EIO;
if (read_ec_data(ideapad_handle, 0x18, &power))
return -EIO;
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = max;
props.type = BACKLIGHT_PLATFORM;
blightdev = backlight_device_register("ideapad",
&priv->platform_device->dev,
priv,
&ideapad_backlight_ops,
&props);
if (IS_ERR(blightdev)) {
pr_err("Could not register backlight device\n");
return PTR_ERR(blightdev);
}
priv->blightdev = blightdev;
blightdev->props.brightness = now;
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
backlight_update_status(blightdev);
return 0;
}
static void ideapad_backlight_exit(struct ideapad_private *priv)
{
if (priv->blightdev)
backlight_device_unregister(priv->blightdev);
priv->blightdev = NULL;
}
static void ideapad_backlight_notify_power(struct ideapad_private *priv)
{
unsigned long power;
struct backlight_device *blightdev = priv->blightdev;
if (read_ec_data(ideapad_handle, 0x18, &power))
return;
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
}
static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
{
unsigned long now;
/* if we control brightness via acpi video driver */
if (priv->blightdev == NULL) {
read_ec_data(ideapad_handle, 0x12, &now);
return;
}
backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
}
/* /*
* module init/exit * module init/exit
*/ */
...@@ -458,8 +552,17 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) ...@@ -458,8 +552,17 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
} }
ideapad_sync_rfk_state(adevice); ideapad_sync_rfk_state(adevice);
if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv);
if (ret && ret != -ENODEV)
goto backlight_failed;
}
return 0; return 0;
backlight_failed:
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(adevice, i);
input_failed: input_failed:
ideapad_platform_exit(priv); ideapad_platform_exit(priv);
platform_failed: platform_failed:
...@@ -472,6 +575,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) ...@@ -472,6 +575,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int i; int i;
ideapad_backlight_exit(priv);
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(adevice, i); ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv); ideapad_input_exit(priv);
...@@ -496,14 +600,21 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ...@@ -496,14 +600,21 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
vpc1 = (vpc2 << 8) | vpc1; vpc1 = (vpc2 << 8) | vpc1;
for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
if (test_bit(vpc_bit, &vpc1)) { if (test_bit(vpc_bit, &vpc1)) {
if (vpc_bit == 9) switch (vpc_bit) {
case 9:
ideapad_sync_rfk_state(adevice); ideapad_sync_rfk_state(adevice);
else if (vpc_bit == 4) break;
read_ec_data(handle, 0x12, &vpc2); case 4:
else ideapad_backlight_notify_brightness(priv);
break;
case 2:
ideapad_backlight_notify_power(priv);
break;
default:
ideapad_input_report(priv, vpc_bit); ideapad_input_report(priv, vpc_bit);
} }
} }
}
} }
static struct acpi_driver ideapad_acpi_driver = { static struct acpi_driver ideapad_acpi_driver = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment