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