[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CACxGe6sAcubJvc6LaVRxt+XzBz0jKpxSAPLdOvXkQz=NkXG5vQ@mail.gmail.com>
Date: Wed, 29 Feb 2012 11:46:39 -0600
From: Grant Likely <grant.likely@...retlab.ca>
To: Seth Forshee <seth.forshee@...onical.com>
Cc: Matthew Garrett <mjg@...hat.com>,
platform-driver-x86@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2] platform/x86: Add driver for Apple gmux device
On Wed, Feb 22, 2012 at 8:37 AM, Seth Forshee
<seth.forshee@...onical.com> wrote:
> Apple laptops with hybrid graphics have a device named gmux that
> controls the muxing of the LVDS panel between the GPUs as well as screen
> brightness. This driver adds support for the gmux device. Only backlight
> control is supported initially.
>
> Signed-off-by: Seth Forshee <seth.forshee@...onical.com>
Works for me.
Tested-by: Grant Likely <grant.likely@...retlab.ca>
Now I just need to figure out how to get the desktop backlight widget
to use gmux_backlight instead of acpi_video0...
g.
> ---
>
> This contains some minor updates to the previous submission. Changes in
> v2:
>
> - Removed unused max_brightness field from struct apple_gmux_data
> - Removed unused gmux_write32() function
> - Mask off unused bits when reading brightness
> - Use gmux_get_brightness() when initializing backlight device
> brightness field
> - Fix some bad grammar in one of the comments.
>
> drivers/platform/x86/Kconfig | 10 ++
> drivers/platform/x86/Makefile | 1 +
> drivers/platform/x86/apple-gmux.c | 228 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 239 insertions(+), 0 deletions(-)
> create mode 100644 drivers/platform/x86/apple-gmux.c
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index f995e6e..a2a9ede 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -779,4 +779,14 @@ config SAMSUNG_Q10
> This driver provides support for backlight control on Samsung Q10
> and related laptops, including Dell Latitude X200.
>
> +config APPLE_GMUX
> + tristate "Apple Gmux Driver"
> + depends on PNP
> + select BACKLIGHT_CLASS_DEVICE
> + ---help---
> + This driver provides support for the gmux device found on many
> + Apple laptops, which controls the display mux for the hybrid
> + graphics as well as the backlight. Currently only backlight
> + control is supported by the driver.
> +
> endif # X86_PLATFORM_DEVICES
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 293a320..a49c891 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -45,3 +45,4 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
> obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
> obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
> obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
> +obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
> diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
> new file mode 100644
> index 0000000..efc1fa9
> --- /dev/null
> +++ b/drivers/platform/x86/apple-gmux.c
> @@ -0,0 +1,228 @@
> +/*
> + * Gmux driver for Apple laptops
> + *
> + * Copyright (C) Canonical Ltd. <seth.forshee@...onical.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/backlight.h>
> +#include <linux/acpi.h>
> +#include <linux/pnp.h>
> +
> +struct apple_gmux_data {
> + unsigned long iostart;
> + unsigned long iolen;
> +
> + struct backlight_device *bdev;
> +};
> +
> +/*
> + * gmux port offsets. Many of these are not yet used, but may be in the
> + * future, and it's useful to have them documented here anyhow.
> + */
> +#define GMUX_PORT_VERSION_MAJOR 0x04
> +#define GMUX_PORT_VERSION_MINOR 0x05
> +#define GMUX_PORT_VERSION_RELEASE 0x06
> +#define GMUX_PORT_SWITCH_DISPLAY 0x10
> +#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
> +#define GMUX_PORT_INTERRUPT_ENABLE 0x14
> +#define GMUX_PORT_INTERRUPT_STATUS 0x16
> +#define GMUX_PORT_SWITCH_DDC 0x28
> +#define GMUX_PORT_SWITCH_EXTERNAL 0x40
> +#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
> +#define GMUX_PORT_DISCRETE_POWER 0x50
> +#define GMUX_PORT_MAX_BRIGHTNESS 0x70
> +#define GMUX_PORT_BRIGHTNESS 0x74
> +
> +#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
> +
> +#define GMUX_INTERRUPT_ENABLE 0xff
> +#define GMUX_INTERRUPT_DISABLE 0x00
> +
> +#define GMUX_INTERRUPT_STATUS_ACTIVE 0
> +#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
> +#define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
> +#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
> +
> +#define GMUX_BRIGHTNESS_MASK 0x00ffffff
> +#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
> +
> +static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
> +{
> + return inb(gmux_data->iostart + port);
> +}
> +
> +static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
> + u8 val)
> +{
> + outb(val, gmux_data->iostart + port);
> +}
> +
> +static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
> +{
> + return inl(gmux_data->iostart + port);
> +}
> +
> +static int gmux_get_brightness(struct backlight_device *bd)
> +{
> + struct apple_gmux_data *gmux_data = bl_get_data(bd);
> + return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
> + GMUX_BRIGHTNESS_MASK;
> +}
> +
> +static int gmux_update_status(struct backlight_device *bd)
> +{
> + struct apple_gmux_data *gmux_data = bl_get_data(bd);
> + u32 brightness = bd->props.brightness;
> +
> + /*
> + * Older gmux versions require writing out lower bytes first then
> + * setting the upper byte to 0 to flush the values. Newer versions
> + * accept a single u32 write, but the old method also works, so we
> + * just use the old method for all gmux versions.
> + */
> + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
> + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
> + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
> + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
> +
> + return 0;
> +}
> +
> +static const struct backlight_ops gmux_bl_ops = {
> + .get_brightness = gmux_get_brightness,
> + .update_status = gmux_update_status,
> +};
> +
> +static int __devinit gmux_probe(struct pnp_dev *pnp,
> + const struct pnp_device_id *id)
> +{
> + struct apple_gmux_data *gmux_data;
> + struct resource *res;
> + struct backlight_properties props;
> + struct backlight_device *bdev;
> + u8 ver_major, ver_minor, ver_release;
> + int ret = -ENXIO;
> +
> + gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
> + if (!gmux_data)
> + return -ENOMEM;
> + pnp_set_drvdata(pnp, gmux_data);
> +
> + res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
> + if (!res) {
> + pr_err("Failed to find gmux I/O resource\n");
> + goto err_free;
> + }
> +
> + gmux_data->iostart = res->start;
> + gmux_data->iolen = res->end - res->start;
> +
> + if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
> + pr_err("gmux I/O region too small (%lu < %u)\n",
> + gmux_data->iolen, GMUX_MIN_IO_LEN);
> + goto err_free;
> + }
> +
> + if (!request_region(gmux_data->iostart, gmux_data->iolen,
> + "Apple gmux")) {
> + pr_err("gmux I/O already in use\n");
> + goto err_free;
> + }
> +
> + /*
> + * On some machines the gmux is in ACPI even thought the machine
> + * doesn't really have a gmux. Check for invalid version information
> + * to detect this.
> + */
> + ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
> + ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
> + ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
> + if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
> + pr_info("gmux device not present\n");
> + ret = -ENODEV;
> + goto err_release;
> + }
> +
> + pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
> + ver_release);
> +
> + memset(&props, 0, sizeof(props));
> + props.type = BACKLIGHT_PLATFORM;
> + props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
> +
> + /*
> + * Currently it's assumed that the maximum brightness is less than
> + * 2^24 for compatibility with old gmux versions. Cap the max
> + * brightness at this value, but print a warning if the hardware
> + * reports something higher so that it can be fixed.
> + */
> + if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
> + props.max_brightness = GMUX_MAX_BRIGHTNESS;
> +
> + bdev = backlight_device_register("gmux_backlight", &pnp->dev,
> + gmux_data, &gmux_bl_ops, &props);
> + if (IS_ERR(bdev)) {
> + ret = PTR_ERR(bdev);
> + goto err_release;
> + }
> +
> + gmux_data->bdev = bdev;
> + bdev->props.brightness = gmux_get_brightness(bdev);
> + backlight_update_status(bdev);
> +
> + return 0;
> +
> +err_release:
> + release_region(gmux_data->iostart, gmux_data->iolen);
> +err_free:
> + kfree(gmux_data);
> + return ret;
> +}
> +
> +static void __devexit gmux_remove(struct pnp_dev *pnp)
> +{
> + struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
> +
> + backlight_device_unregister(gmux_data->bdev);
> + release_region(gmux_data->iostart, gmux_data->iolen);
> + kfree(gmux_data);
> +}
> +
> +static const struct pnp_device_id gmux_device_ids[] = {
> + {"APP000B", 0},
> + {"", 0}
> +};
> +
> +static struct pnp_driver gmux_pnp_driver = {
> + .name = "apple-gmux",
> + .probe = gmux_probe,
> + .remove = __devexit_p(gmux_remove),
> + .id_table = gmux_device_ids,
> +};
> +
> +static int __init apple_gmux_init(void)
> +{
> + return pnp_register_driver(&gmux_pnp_driver);
> +}
> +
> +static void __exit apple_gmux_exit(void)
> +{
> + pnp_unregister_driver(&gmux_pnp_driver);
> +}
> +
> +module_init(apple_gmux_init);
> +module_exit(apple_gmux_exit);
> +
> +MODULE_AUTHOR("Seth Forshee <seth.forshee@...onical.com>");
> +MODULE_DESCRIPTION("Apple Gmux Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_DEVICE_TABLE(pnp, gmux_device_ids);
> --
> 1.7.9
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists