Commit 96ff7056 authored by Matthew Garrett's avatar Matthew Garrett

apple_gmux: Add support for newer hardware

New gmux devices have a different method for accessing the registers.
Update the driver to cope. Incorporates feedback from Bernhard Froemel.
Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
Cc: Bernhard Froemel <froemel@vmars.tuwien.ac.at>
Cc: Seth Forshee <seth.forshee@canonical.com>
parent 7e30ed6b
...@@ -18,12 +18,15 @@ ...@@ -18,12 +18,15 @@
#include <linux/pnp.h> #include <linux/pnp.h>
#include <linux/apple_bl.h> #include <linux/apple_bl.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h>
#include <acpi/video.h> #include <acpi/video.h>
#include <asm/io.h> #include <asm/io.h>
struct apple_gmux_data { struct apple_gmux_data {
unsigned long iostart; unsigned long iostart;
unsigned long iolen; unsigned long iolen;
bool indexed;
struct mutex index_lock;
struct backlight_device *bdev; struct backlight_device *bdev;
}; };
...@@ -45,6 +48,9 @@ struct apple_gmux_data { ...@@ -45,6 +48,9 @@ struct apple_gmux_data {
#define GMUX_PORT_DISCRETE_POWER 0x50 #define GMUX_PORT_DISCRETE_POWER 0x50
#define GMUX_PORT_MAX_BRIGHTNESS 0x70 #define GMUX_PORT_MAX_BRIGHTNESS 0x70
#define GMUX_PORT_BRIGHTNESS 0x74 #define GMUX_PORT_BRIGHTNESS 0x74
#define GMUX_PORT_VALUE 0xc2
#define GMUX_PORT_READ 0xd0
#define GMUX_PORT_WRITE 0xd4
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
...@@ -59,23 +65,23 @@ struct apple_gmux_data { ...@@ -59,23 +65,23 @@ struct apple_gmux_data {
#define GMUX_BRIGHTNESS_MASK 0x00ffffff #define GMUX_BRIGHTNESS_MASK 0x00ffffff
#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK #define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
{ {
return inb(gmux_data->iostart + port); return inb(gmux_data->iostart + port);
} }
static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port, static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
u8 val) u8 val)
{ {
outb(val, gmux_data->iostart + port); outb(val, gmux_data->iostart + port);
} }
static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
{ {
return inl(gmux_data->iostart + port); return inl(gmux_data->iostart + port);
} }
static inline u32 gmux_write32(struct apple_gmux_data *gmux_data, int port, static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
u32 val) u32 val)
{ {
int i; int i;
...@@ -87,6 +93,144 @@ static inline u32 gmux_write32(struct apple_gmux_data *gmux_data, int port, ...@@ -87,6 +93,144 @@ static inline u32 gmux_write32(struct apple_gmux_data *gmux_data, int port,
} }
} }
static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
{
int i = 200;
u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
while (i && (gwr & 0x01)) {
inb(gmux_data->iostart + GMUX_PORT_READ);
gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
udelay(100);
i--;
}
return !!i;
}
static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
{
int i = 200;
u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
while (i && !(gwr & 0x01)) {
gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
udelay(100);
i--;
}
if (gwr & 0x01)
inb(gmux_data->iostart + GMUX_PORT_READ);
return !!i;
}
static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
{
u8 val;
mutex_lock(&gmux_data->index_lock);
outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
gmux_index_wait_ready(gmux_data);
val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
mutex_unlock(&gmux_data->index_lock);
return val;
}
static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
u8 val)
{
mutex_lock(&gmux_data->index_lock);
outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
gmux_index_wait_ready(gmux_data);
outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
gmux_index_wait_complete(gmux_data);
mutex_unlock(&gmux_data->index_lock);
}
static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
{
u32 val;
mutex_lock(&gmux_data->index_lock);
outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
gmux_index_wait_ready(gmux_data);
val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
mutex_unlock(&gmux_data->index_lock);
return val;
}
static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
u32 val)
{
int i;
u8 tmpval;
mutex_lock(&gmux_data->index_lock);
for (i = 0; i < 4; i++) {
tmpval = (val >> (i * 8)) & 0xff;
outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
}
gmux_index_wait_ready(gmux_data);
outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
gmux_index_wait_complete(gmux_data);
mutex_unlock(&gmux_data->index_lock);
}
static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
{
if (gmux_data->indexed)
return gmux_index_read8(gmux_data, port);
else
return gmux_pio_read8(gmux_data, port);
}
static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
{
if (gmux_data->indexed)
gmux_index_write8(gmux_data, port, val);
else
gmux_pio_write8(gmux_data, port, val);
}
static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
{
if (gmux_data->indexed)
return gmux_index_read32(gmux_data, port);
else
return gmux_pio_read32(gmux_data, port);
}
static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
u32 val)
{
if (gmux_data->indexed)
gmux_index_write32(gmux_data, port, val);
else
gmux_pio_write32(gmux_data, port, val);
}
static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
{
u16 val;
outb(0xaa, gmux_data->iostart + 0xcc);
outb(0x55, gmux_data->iostart + 0xcd);
outb(0x00, gmux_data->iostart + 0xce);
val = inb(gmux_data->iostart + 0xcc) |
(inb(gmux_data->iostart + 0xcd) << 8);
if (val == 0x55aa)
return true;
return false;
}
static int gmux_get_brightness(struct backlight_device *bd) static int gmux_get_brightness(struct backlight_device *bd)
{ {
struct apple_gmux_data *gmux_data = bl_get_data(bd); struct apple_gmux_data *gmux_data = bl_get_data(bd);
...@@ -150,21 +294,28 @@ static int __devinit gmux_probe(struct pnp_dev *pnp, ...@@ -150,21 +294,28 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
} }
/* /*
* On some machines the gmux is in ACPI even thought the machine * Invalid version information may indicate either that the gmux
* doesn't really have a gmux. Check for invalid version information * device isn't present or that it's a new one that uses indexed
* to detect this. * io
*/ */
ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR); ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR); ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
if (gmux_is_indexed(gmux_data)) {
mutex_init(&gmux_data->index_lock);
gmux_data->indexed = true;
} else {
pr_info("gmux device not present\n"); pr_info("gmux device not present\n");
ret = -ENODEV; ret = -ENODEV;
goto err_release; goto err_release;
} }
pr_info("Found indexed gmux\n");
} else {
pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor, pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
ver_release); ver_release);
}
memset(&props, 0, sizeof(props)); memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM; props.type = BACKLIGHT_PLATFORM;
......
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