[go: nahoru, domu]

1917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee/*
2917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *  Gmux driver for Apple laptops
3917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *
4917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *  Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider *  Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
6917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *
7917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *  This program is free software; you can redistribute it and/or modify
8917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *  it under the terms of the GNU General Public License version 2 as
9917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee *  published by the Free Software Foundation.
10917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee */
11917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
12917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
14917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/module.h>
15917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/kernel.h>
16917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/init.h>
17917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/backlight.h>
18917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/acpi.h>
19917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/pnp.h>
20917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/apple_bl.h>
21917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <linux/slab.h>
2296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett#include <linux/delay.h>
2376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider#include <linux/pci.h>
2476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider#include <linux/vga_switcheroo.h>
25917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <acpi/video.h>
26917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#include <asm/io.h>
27917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
28917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestruct apple_gmux_data {
29917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	unsigned long iostart;
30917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	unsigned long iolen;
3196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	bool indexed;
3296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	struct mutex index_lock;
33917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
34917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct backlight_device *bdev;
3576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
3676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	/* switcheroo data */
3776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	acpi_handle dhandle;
3876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	int gpe;
3976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	enum vga_switcheroo_client_id resume_client_id;
4076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	enum vga_switcheroo_state power_state;
4176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	struct completion powerchange_done;
42917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee};
43917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
4476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic struct apple_gmux_data *apple_gmux_data;
4576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
46917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee/*
47917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee * gmux port offsets. Many of these are not yet used, but may be in the
48917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee * future, and it's useful to have them documented here anyhow.
49917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee */
50917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_VERSION_MAJOR		0x04
51917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_VERSION_MINOR		0x05
52917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_VERSION_RELEASE	0x06
53917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_SWITCH_DISPLAY	0x10
54917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_SWITCH_GET_DISPLAY	0x11
55917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_INTERRUPT_ENABLE	0x14
56917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_INTERRUPT_STATUS	0x16
57917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_SWITCH_DDC		0x28
58917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_SWITCH_EXTERNAL	0x40
59917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_SWITCH_GET_EXTERNAL	0x41
60917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_DISCRETE_POWER	0x50
61917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_MAX_BRIGHTNESS	0x70
62917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_PORT_BRIGHTNESS		0x74
6396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett#define GMUX_PORT_VALUE			0xc2
6496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett#define GMUX_PORT_READ			0xd0
6596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett#define GMUX_PORT_WRITE			0xd4
66917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
67917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_MIN_IO_LEN			(GMUX_PORT_BRIGHTNESS + 4)
68917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
69917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_INTERRUPT_ENABLE		0xff
70917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_INTERRUPT_DISABLE		0x00
71917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
72917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_INTERRUPT_STATUS_ACTIVE	0
73917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_INTERRUPT_STATUS_DISPLAY	(1 << 0)
74917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_INTERRUPT_STATUS_POWER	(1 << 2)
75917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_INTERRUPT_STATUS_HOTPLUG	(1 << 3)
76917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
77917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_BRIGHTNESS_MASK		0x00ffffff
78917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee#define GMUX_MAX_BRIGHTNESS		GMUX_BRIGHTNESS_MASK
79917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
8096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
81917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
82917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return inb(gmux_data->iostart + port);
83917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
84917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
8596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
86917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee			       u8 val)
87917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
88917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	outb(val, gmux_data->iostart + port);
89917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
90917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
9196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
92917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
93917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return inl(gmux_data->iostart + port);
94917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
95917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
9696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
9796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			     u32 val)
987e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett{
997e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett	int i;
1007e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett	u8 tmpval;
1017e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett
1027e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett	for (i = 0; i < 4; i++) {
1037e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett		tmpval = (val >> (i * 8)) & 0xff;
104e6d9d3d59ca08fc87688c5953061b4da0d17bf15Seth Forshee		outb(tmpval, gmux_data->iostart + port + i);
1057e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett	}
1067e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett}
1077e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett
10896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
10996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
11096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	int i = 200;
11196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
11296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
11396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	while (i && (gwr & 0x01)) {
11496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		inb(gmux_data->iostart + GMUX_PORT_READ);
11596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
11696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		udelay(100);
11796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		i--;
11896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	}
11996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
12096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	return !!i;
12196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
12296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
12396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
12496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
12596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	int i = 200;
12696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
12796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
12896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	while (i && !(gwr & 0x01)) {
12996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
13096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		udelay(100);
13196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		i--;
13296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	}
13396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
13496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	if (gwr & 0x01)
13596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		inb(gmux_data->iostart + GMUX_PORT_READ);
13696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
13796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	return !!i;
13896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
13996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
14096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
14196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
14296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	u8 val;
14396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
14496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_lock(&gmux_data->index_lock);
14596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	gmux_index_wait_ready(gmux_data);
146c5a5052923c55990e32a3d64bdb4779b01162646Bernhard Froemel	outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
147c5a5052923c55990e32a3d64bdb4779b01162646Bernhard Froemel	gmux_index_wait_complete(gmux_data);
14896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
14996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_unlock(&gmux_data->index_lock);
15096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
15196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	return val;
15296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
15396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
15496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
15596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			      u8 val)
15696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
15796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_lock(&gmux_data->index_lock);
15896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
15996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	gmux_index_wait_ready(gmux_data);
16096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
16196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	gmux_index_wait_complete(gmux_data);
16296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_unlock(&gmux_data->index_lock);
16396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
16496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
16596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
16696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
16796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	u32 val;
16896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
16996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_lock(&gmux_data->index_lock);
17096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	gmux_index_wait_ready(gmux_data);
171c5a5052923c55990e32a3d64bdb4779b01162646Bernhard Froemel	outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
172c5a5052923c55990e32a3d64bdb4779b01162646Bernhard Froemel	gmux_index_wait_complete(gmux_data);
17396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
17496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_unlock(&gmux_data->index_lock);
17596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
17696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	return val;
17796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
17896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
17996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
18096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			       u32 val)
18196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
18296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	int i;
18396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	u8 tmpval;
18496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
18596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_lock(&gmux_data->index_lock);
18696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
18796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	for (i = 0; i < 4; i++) {
18896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		tmpval = (val >> (i * 8)) & 0xff;
18996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
19096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	}
19196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
19296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	gmux_index_wait_ready(gmux_data);
19396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
19496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	gmux_index_wait_complete(gmux_data);
19596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	mutex_unlock(&gmux_data->index_lock);
19696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
19796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
19896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
19996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
20096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	if (gmux_data->indexed)
20196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		return gmux_index_read8(gmux_data, port);
20296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	else
20396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		return gmux_pio_read8(gmux_data, port);
20496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
20596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
20696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
20796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
20896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	if (gmux_data->indexed)
20996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		gmux_index_write8(gmux_data, port, val);
21096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	else
21196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		gmux_pio_write8(gmux_data, port, val);
21296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
21396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
21496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
21596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
21696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	if (gmux_data->indexed)
21796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		return gmux_index_read32(gmux_data, port);
21896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	else
21996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		return gmux_pio_read32(gmux_data, port);
22096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
22196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
22296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic void gmux_write32(struct apple_gmux_data *gmux_data, int port,
22396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			     u32 val)
22496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
22596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	if (gmux_data->indexed)
22696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		gmux_index_write32(gmux_data, port, val);
22796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	else
22896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		gmux_pio_write32(gmux_data, port, val);
22996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
23096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
23196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrettstatic bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
23296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett{
23396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	u16 val;
23496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
23596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	outb(0xaa, gmux_data->iostart + 0xcc);
23696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	outb(0x55, gmux_data->iostart + 0xcd);
23796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	outb(0x00, gmux_data->iostart + 0xce);
23896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
23996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	val = inb(gmux_data->iostart + 0xcc) |
24096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		(inb(gmux_data->iostart + 0xcd) << 8);
24196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
24296ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	if (val == 0x55aa)
24396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		return true;
24496ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
24596ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	return false;
24696ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett}
24796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
248917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic int gmux_get_brightness(struct backlight_device *bd)
249917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
250917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct apple_gmux_data *gmux_data = bl_get_data(bd);
251917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
252917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	       GMUX_BRIGHTNESS_MASK;
253917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
254917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
255917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic int gmux_update_status(struct backlight_device *bd)
256917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
257917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct apple_gmux_data *gmux_data = bl_get_data(bd);
258917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	u32 brightness = bd->props.brightness;
259917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
26096960880c31131ab906e7e0627ab89e8319c385eSeth Forshee	if (bd->props.state & BL_CORE_SUSPENDED)
261a2f01a899347fd97cb18094e5a55640cab552818Matthew Garrett		return 0;
26296960880c31131ab906e7e0627ab89e8319c385eSeth Forshee
2637e30ed6bdd91ae73c34fc37b57fcccc8640641f9Matthew Garrett	gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
264917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
265917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return 0;
266917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
267917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
268917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic const struct backlight_ops gmux_bl_ops = {
26996960880c31131ab906e7e0627ab89e8319c385eSeth Forshee	.options = BL_CORE_SUSPENDRESUME,
270917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	.get_brightness = gmux_get_brightness,
271917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	.update_status = gmux_update_status,
272917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee};
273917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
27476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic int gmux_switchto(enum vga_switcheroo_client_id id)
27576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
27676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (id == VGA_SWITCHEROO_IGD) {
27776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
27876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
27976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
28076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	} else {
28176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
28276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
28376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
28476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	}
28576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
28676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return 0;
28776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
28876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
28976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
29076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider				   enum vga_switcheroo_state state)
29176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
29216735d022f72b20ddbb2274b8e109f69575e9b2bWolfram Sang	reinit_completion(&gmux_data->powerchange_done);
29376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
29476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (state == VGA_SWITCHEROO_ON) {
29576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
29676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
29776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		pr_debug("Discrete card powered up\n");
29876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	} else {
29976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
30076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
30176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		pr_debug("Discrete card powered down\n");
30276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	}
30376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
30476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_data->power_state = state;
30576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
30676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (gmux_data->gpe >= 0 &&
30776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	    !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
30876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider						       msecs_to_jiffies(200)))
30976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		pr_warn("Timeout waiting for gmux switch to complete\n");
31076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
31176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return 0;
31276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
31376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
31476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic int gmux_set_power_state(enum vga_switcheroo_client_id id,
31576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider				enum vga_switcheroo_state state)
31676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
31776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (id == VGA_SWITCHEROO_IGD)
31876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		return 0;
31976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
32076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return gmux_set_discrete_state(apple_gmux_data, state);
32176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
32276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
32376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic int gmux_get_client_id(struct pci_dev *pdev)
32476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
32576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	/*
32676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	 * Early Macbook Pros with switchable graphics use nvidia
32776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	 * integrated graphics. Hardcode that the 9400M is integrated.
32876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	 */
32976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
33076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		return VGA_SWITCHEROO_IGD;
33176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
33276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		 pdev->device == 0x0863)
33376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		return VGA_SWITCHEROO_IGD;
33476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	else
33576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		return VGA_SWITCHEROO_DIS;
33676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
33776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
33876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic enum vga_switcheroo_client_id
33976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heidergmux_active_client(struct apple_gmux_data *gmux_data)
34076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
34176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
34276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		return VGA_SWITCHEROO_IGD;
34376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
34476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return VGA_SWITCHEROO_DIS;
34576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
34676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
34776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic struct vga_switcheroo_handler gmux_handler = {
34876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	.switchto = gmux_switchto,
34976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	.power_state = gmux_set_power_state,
35076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	.get_client_id = gmux_get_client_id,
35176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider};
35276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
35376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
35476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
35576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
35676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		    GMUX_INTERRUPT_DISABLE);
35776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
35876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
35976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
36076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
36176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
36276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		    GMUX_INTERRUPT_ENABLE);
36376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
36476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
36576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
36676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
36776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
36876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
36976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
37076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
37176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
37276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	u8 status;
37376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
37476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	/* to clear interrupts write back current status */
37576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	status = gmux_interrupt_get_status(gmux_data);
37676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
37776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
37876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
37976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heiderstatic void gmux_notify_handler(acpi_handle device, u32 value, void *context)
38076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
38176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	u8 status;
38276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	struct pnp_dev *pnp = (struct pnp_dev *)context;
38376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
38476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
38576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	status = gmux_interrupt_get_status(gmux_data);
38676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_disable_interrupts(gmux_data);
38776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	pr_debug("Notify handler called: status %d\n", status);
38876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
38976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_clear_interrupts(gmux_data);
39076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_enable_interrupts(gmux_data);
39176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
39276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (status & GMUX_INTERRUPT_STATUS_POWER)
39376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		complete(&gmux_data->powerchange_done);
39476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
39576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
3968aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khanstatic int gmux_suspend(struct device *dev)
39776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
3988aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan	struct pnp_dev *pnp = to_pnp_dev(dev);
39976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
4008aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan
40176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_data->resume_client_id = gmux_active_client(gmux_data);
40276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_disable_interrupts(gmux_data);
40376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return 0;
40476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
40576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
4068aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khanstatic int gmux_resume(struct device *dev)
40776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider{
4088aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan	struct pnp_dev *pnp = to_pnp_dev(dev);
40976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
4108aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan
41176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_enable_interrupts(gmux_data);
41276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_switchto(gmux_data->resume_client_id);
41376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
41476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_set_discrete_state(gmux_data, gmux_data->power_state);
41576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	return 0;
41676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider}
41776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
418b859f15921321b7bf4cb4cf188e31a6a25d2dff3Greg Kroah-Hartmanstatic int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
419917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
420917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct apple_gmux_data *gmux_data;
421917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct resource *res;
422917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct backlight_properties props;
423917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct backlight_device *bdev;
424917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	u8 ver_major, ver_minor, ver_release;
425917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	int ret = -ENXIO;
42676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	acpi_status status;
42776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	unsigned long long gpe;
42876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
42976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (apple_gmux_data)
43076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		return -EBUSY;
431917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
432917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
433917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (!gmux_data)
434917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		return -ENOMEM;
435917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	pnp_set_drvdata(pnp, gmux_data);
436917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
437917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
438917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (!res) {
439917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		pr_err("Failed to find gmux I/O resource\n");
440917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		goto err_free;
441917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	}
442917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
443917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	gmux_data->iostart = res->start;
444917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	gmux_data->iolen = res->end - res->start;
445917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
446917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
447917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		pr_err("gmux I/O region too small (%lu < %u)\n",
448917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		       gmux_data->iolen, GMUX_MIN_IO_LEN);
449917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		goto err_free;
450917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	}
451917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
452917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (!request_region(gmux_data->iostart, gmux_data->iolen,
453917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee			    "Apple gmux")) {
454917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		pr_err("gmux I/O already in use\n");
455917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		goto err_free;
456917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	}
457917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
458917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	/*
45996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	 * Invalid version information may indicate either that the gmux
46096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	 * device isn't present or that it's a new one that uses indexed
46196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett	 * io
462917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 */
46396ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett
464917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
465917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
466917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
467917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
46896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		if (gmux_is_indexed(gmux_data)) {
46907f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel			u32 version;
47096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			mutex_init(&gmux_data->index_lock);
47196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			gmux_data->indexed = true;
47207f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel			version = gmux_read32(gmux_data,
47307f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel				GMUX_PORT_VERSION_MAJOR);
47407f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel			ver_major = (version >> 24) & 0xff;
47507f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel			ver_minor = (version >> 16) & 0xff;
47607f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel			ver_release = (version >> 8) & 0xff;
47796ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		} else {
47896ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			pr_info("gmux device not present\n");
47996ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			ret = -ENODEV;
48096ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett			goto err_release;
48196ff705638e3d61b1e45a047c0f9f3bb622fa32fMatthew Garrett		}
482917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	}
48307f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel	pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
48407f377da7e8a7d3c3a6626333516f9c808637c9eBernhard Froemel		ver_release, (gmux_data->indexed ? "indexed" : "classic"));
485917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
486917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	memset(&props, 0, sizeof(props));
487917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	props.type = BACKLIGHT_PLATFORM;
488917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
489917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
490917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	/*
491917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * Currently it's assumed that the maximum brightness is less than
492917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * 2^24 for compatibility with old gmux versions. Cap the max
493917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * brightness at this value, but print a warning if the hardware
494917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * reports something higher so that it can be fixed.
495917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 */
496917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
497917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		props.max_brightness = GMUX_MAX_BRIGHTNESS;
498917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
499917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	bdev = backlight_device_register("gmux_backlight", &pnp->dev,
500917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee					 gmux_data, &gmux_bl_ops, &props);
501917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	if (IS_ERR(bdev)) {
502917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		ret = PTR_ERR(bdev);
503917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee		goto err_release;
504917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	}
505917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
506917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	gmux_data->bdev = bdev;
507917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	bdev->props.brightness = gmux_get_brightness(bdev);
508917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	backlight_update_status(bdev);
509917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
510917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	/*
511917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * The backlight situation on Macs is complicated. If the gmux is
512917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * present it's the best choice, because it always works for
513917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * backlight control and supports more levels than other options.
514917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 * Disable the other backlight choices.
515917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	 */
516a60b21763cce01c64cc537869662b41429c62e5fCorentin Chary	acpi_video_dmi_promote_vendor();
517917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	acpi_video_unregister();
518917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	apple_bl_unregister();
519917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
52076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_data->power_state = VGA_SWITCHEROO_ON;
52176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
5223a83f992490f8235661b768e53bd5f14915420acRafael J. Wysocki	gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
52376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (!gmux_data->dhandle) {
52476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		pr_err("Cannot find acpi handle for pnp device %s\n",
52576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		       dev_name(&pnp->dev));
52676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		ret = -ENODEV;
52776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		goto err_notify;
52876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	}
52976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
53076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
53176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (ACPI_SUCCESS(status)) {
53276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_data->gpe = (int)gpe;
53376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
53476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		status = acpi_install_notify_handler(gmux_data->dhandle,
53576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider						     ACPI_DEVICE_NOTIFY,
53676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider						     &gmux_notify_handler, pnp);
53776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		if (ACPI_FAILURE(status)) {
53876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			pr_err("Install notify handler failed: %s\n",
53976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			       acpi_format_exception(status));
54076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			ret = -ENODEV;
54176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			goto err_notify;
54276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		}
54376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
54476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		status = acpi_enable_gpe(NULL, gmux_data->gpe);
54576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		if (ACPI_FAILURE(status)) {
54676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			pr_err("Cannot enable gpe: %s\n",
54776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			       acpi_format_exception(status));
54876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider			goto err_enable_gpe;
54976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		}
55076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	} else {
55176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		pr_warn("No GPE found for gmux\n");
55276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		gmux_data->gpe = -1;
55376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	}
55476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
55576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (vga_switcheroo_register_handler(&gmux_handler)) {
55676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		ret = -ENODEV;
55776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		goto err_register_handler;
55876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	}
55976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
56076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	init_completion(&gmux_data->powerchange_done);
56176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	apple_gmux_data = gmux_data;
56276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_enable_interrupts(gmux_data);
56376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
564917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return 0;
565917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
56676b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heidererr_register_handler:
56776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (gmux_data->gpe >= 0)
56876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		acpi_disable_gpe(NULL, gmux_data->gpe);
56976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heidererr_enable_gpe:
57076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (gmux_data->gpe >= 0)
57176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		acpi_remove_notify_handler(gmux_data->dhandle,
57276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider					   ACPI_DEVICE_NOTIFY,
57376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider					   &gmux_notify_handler);
57476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heidererr_notify:
57576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	backlight_device_unregister(bdev);
576917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheeerr_release:
577917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	release_region(gmux_data->iostart, gmux_data->iolen);
578917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheeerr_free:
579917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	kfree(gmux_data);
580917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return ret;
581917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
582917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
583b859f15921321b7bf4cb4cf188e31a6a25d2dff3Greg Kroah-Hartmanstatic void gmux_remove(struct pnp_dev *pnp)
584917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
585917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
586917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
58776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	vga_switcheroo_unregister_handler();
58876b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	gmux_disable_interrupts(gmux_data);
58976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	if (gmux_data->gpe >= 0) {
59076b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		acpi_disable_gpe(NULL, gmux_data->gpe);
59176b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider		acpi_remove_notify_handler(gmux_data->dhandle,
59276b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider					   ACPI_DEVICE_NOTIFY,
59376b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider					   &gmux_notify_handler);
59476b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	}
59576b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
596917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	backlight_device_unregister(gmux_data->bdev);
59776b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider
598917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	release_region(gmux_data->iostart, gmux_data->iolen);
59976b487dd5187a4d7cc6eccd452f65467a8c7768bAndreas Heider	apple_gmux_data = NULL;
600917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	kfree(gmux_data);
601917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
602a60b21763cce01c64cc537869662b41429c62e5fCorentin Chary	acpi_video_dmi_demote_vendor();
603917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	acpi_video_register();
604917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	apple_bl_register();
605917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
606917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
607917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic const struct pnp_device_id gmux_device_ids[] = {
608917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	{"APP000B", 0},
609917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	{"", 0}
610917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee};
611917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
6128aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khanstatic const struct dev_pm_ops gmux_dev_pm_ops = {
6138aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan	.suspend = gmux_suspend,
6148aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan	.resume = gmux_resume,
6158aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan};
6168aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan
617917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic struct pnp_driver gmux_pnp_driver = {
618917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	.name		= "apple-gmux",
619917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	.probe		= gmux_probe,
620b859f15921321b7bf4cb4cf188e31a6a25d2dff3Greg Kroah-Hartman	.remove		= gmux_remove,
621917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	.id_table	= gmux_device_ids,
6228aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan	.driver		= {
6238aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan			.pm = &gmux_dev_pm_ops,
6248aa6c2166b5184fb2344062cf2fa229b197c1f84Shuah Khan	},
625917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee};
626917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
627917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic int __init apple_gmux_init(void)
628917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
629917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	return pnp_register_driver(&gmux_pnp_driver);
630917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
631917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
632917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheestatic void __exit apple_gmux_exit(void)
633917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee{
634917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee	pnp_unregister_driver(&gmux_pnp_driver);
635917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee}
636917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
637917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheemodule_init(apple_gmux_init);
638917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forsheemodule_exit(apple_gmux_exit);
639917ee75a59160fe3518c1672feb4562f11a18fbcSeth Forshee
640917ee75a59160fe3518c1672feb4562f11a18fbcSeth ForsheeMODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
641917ee75a59160fe3518c1672feb4562f11a18fbcSeth ForsheeMODULE_DESCRIPTION("Apple Gmux Driver");
642917ee75a59160fe3518c1672feb4562f11a18fbcSeth ForsheeMODULE_LICENSE("GPL");
643917ee75a59160fe3518c1672feb4562f11a18fbcSeth ForsheeMODULE_DEVICE_TABLE(pnp, gmux_device_ids);
644