|
@@ -32,6 +32,8 @@
|
|
|
#include <linux/platform_device.h>
|
|
|
#include <linux/input.h>
|
|
|
#include <linux/input/sparse-keymap.h>
|
|
|
+#include <linux/backlight.h>
|
|
|
+#include <linux/fb.h>
|
|
|
|
|
|
#define IDEAPAD_RFKILL_DEV_NUM (3)
|
|
|
|
|
@@ -44,6 +46,7 @@ struct ideapad_private {
|
|
|
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
|
|
|
struct platform_device *platform_device;
|
|
|
struct input_dev *inputdev;
|
|
|
+ struct backlight_device *blightdev;
|
|
|
unsigned long cfg;
|
|
|
};
|
|
|
|
|
@@ -309,8 +312,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
|
|
|
- int dev)
|
|
|
+static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
|
|
|
{
|
|
|
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
|
|
|
|
|
@@ -417,6 +419,98 @@ static void ideapad_input_report(struct ideapad_private *priv,
|
|
|
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
|
|
|
*/
|
|
@@ -458,8 +552,17 @@ static int __devinit ideapad_acpi_add(struct acpi_device *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;
|
|
|
|
|
|
+backlight_failed:
|
|
|
+ for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
|
|
|
+ ideapad_unregister_rfkill(adevice, i);
|
|
|
input_failed:
|
|
|
ideapad_platform_exit(priv);
|
|
|
platform_failed:
|
|
@@ -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);
|
|
|
int i;
|
|
|
|
|
|
+ ideapad_backlight_exit(priv);
|
|
|
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
|
|
|
ideapad_unregister_rfkill(adevice, i);
|
|
|
ideapad_input_exit(priv);
|
|
@@ -496,12 +600,19 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
|
|
|
vpc1 = (vpc2 << 8) | vpc1;
|
|
|
for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
|
|
|
if (test_bit(vpc_bit, &vpc1)) {
|
|
|
- if (vpc_bit == 9)
|
|
|
+ switch (vpc_bit) {
|
|
|
+ case 9:
|
|
|
ideapad_sync_rfk_state(adevice);
|
|
|
- else if (vpc_bit == 4)
|
|
|
- read_ec_data(handle, 0x12, &vpc2);
|
|
|
- else
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ ideapad_backlight_notify_brightness(priv);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ ideapad_backlight_notify_power(priv);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
ideapad_input_report(priv, vpc_bit);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|