Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+117 −6
Original line number Diff line number Diff line
@@ -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,15 +600,22 @@ 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);
			}
		}
	}
}

static struct acpi_driver ideapad_acpi_driver = {
	.name = "ideapad_acpi",