1
/*
2
 *  Backlight Driver for Nvidia 8600 in Macbook Pro
3
 *
4
 *  Copyright (c) Red Hat <mjg@redhat.com>
5
 *  Based on code from Pommed:
6
 *  Copyright (C) 2006 Nicolas Boichat <nicolas @boichat.ch>
7
 *  Copyright (C) 2006 Felipe Alfaro Solana <felipe_alfaro @linuxmail.org>
8
 *  Copyright (C) 2007 Julien BLACHE <jb@jblache.org>
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License version 2 as
12
 *  published by the Free Software Foundation.
13
 *
14
 *  This driver triggers SMIs which cause the firmware to change the
15
 *  backlight brightness. This is icky in many ways, but it's impractical to
16
 *  get at the firmware code in order to figure out what it's actually doing.
17
 */
18
19
#include <linux/module.h>
20
#include <linux/kernel.h>
21
#include <linux/init.h>
22
#include <linux/platform_device.h>
23
#include <linux/backlight.h>
24
#include <linux/err.h>
25
#include <linux/dmi.h>
26
#include <linux/io.h>
27
28
static struct backlight_device *mbp_backlight_device;
29
30
/* Structure to be passed to the DMI_MATCH function. */
31
struct dmi_match_data {
32
	/* I/O resource to allocate. */
33
	unsigned long iostart;
34
	unsigned long iolen;
35
	/* Backlight operations structure. */
36
	struct backlight_ops backlight_ops;
37
};
38
39
/* Module parameters. */
40
static int debug;
41
module_param_named(debug, debug, int, 0644);
42
MODULE_PARM_DESC(debug, "Set to one to enable debugging messages.");
43
44
/*
45
 * Implementation for MacBooks with Intel chipset.
46
 */
47
static int intel_chipset_send_intensity(struct backlight_device *bd)
48
{
49
	int intensity = bd->props.brightness;
50
51
	if (debug)
52
		printk(KERN_DEBUG "mbp_nvidia_bl: setting brightness to %d\n",
53
		       intensity);
54
55
	outb(0x04 | (intensity << 4), 0xb3);
56
	outb(0xbf, 0xb2);
57
	return 0;
58
}
59
60
static int intel_chipset_get_intensity(struct backlight_device *bd)
61
{
62
	int intensity;
63
64
	outb(0x03, 0xb3);
65
	outb(0xbf, 0xb2);
66
	intensity = inb(0xb3) >> 4;
67
68
	if (debug)
69
		printk(KERN_DEBUG "mbp_nvidia_bl: read brightness of %d\n",
70
		       intensity);
71
72
	return intensity;
73
}
74
75
static const struct dmi_match_data intel_chipset_data = {
76
	.iostart = 0xb2,
77
	.iolen = 2,
78
	.backlight_ops	= {
79
		.options	= BL_CORE_SUSPENDRESUME,
80
		.get_brightness	= intel_chipset_get_intensity,
81
		.update_status	= intel_chipset_send_intensity,
82
	}
83
};
84
85
/*
86
 * Implementation for MacBooks with Nvidia chipset.
87
 */
88
static int nvidia_chipset_send_intensity(struct backlight_device *bd)
89
{
90
	int intensity = bd->props.brightness;
91
92
	if (debug)
93
		printk(KERN_DEBUG "mbp_nvidia_bl: setting brightness to %d\n",
94
		       intensity);
95
96
	outb(0x04 | (intensity << 4), 0x52f);
97
	outb(0xbf, 0x52e);
98
	return 0;
99
}
100
101
static int nvidia_chipset_get_intensity(struct backlight_device *bd)
102
{
103
	int intensity;
104
105
	outb(0x03, 0x52f);
106
	outb(0xbf, 0x52e);
107
	intensity = inb(0x52f) >> 4;
108
109
	if (debug)
110
		printk(KERN_DEBUG "mbp_nvidia_bl: read brightness of %d\n",
111
		       intensity);
112
113
	return intensity;
114
}
115
116
static const struct dmi_match_data nvidia_chipset_data = {
117
	.iostart = 0x52e,
118
	.iolen = 2,
119
	.backlight_ops		= {
120
		.options	= BL_CORE_SUSPENDRESUME,
121
		.get_brightness	= nvidia_chipset_get_intensity,
122
		.update_status	= nvidia_chipset_send_intensity
123
	}
124
};
125
126
/*
127
 * DMI matching.
128
 */
129
static /* const */ struct dmi_match_data *driver_data;
130
131
static int mbp_dmi_match(const struct dmi_system_id *id)
132
{
133
	driver_data = id->driver_data;
134
135
	printk(KERN_INFO "mbp_nvidia_bl: %s detected\n", id->ident);
136
	return 1;
137
}
138
139
static const struct dmi_system_id __initdata mbp_device_table[] = {
140
	{
141
		.callback	= mbp_dmi_match,
142
		.ident		= "MacBookPro 3,1",
143
		.matches	= {
144
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
145
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
146
		},
147
		.driver_data	= (void *)&intel_chipset_data,
148
	},
149
	{
150
		.callback	= mbp_dmi_match,
151
		.ident		= "MacBookPro 3,2",
152
		.matches	= {
153
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
154
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"),
155
		},
156
		.driver_data	= (void *)&intel_chipset_data,
157
	},
158
	{
159
		.callback	= mbp_dmi_match,
160
		.ident		= "MacBookPro 4,1",
161
		.matches	= {
162
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
163
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"),
164
		},
165
		.driver_data	= (void *)&intel_chipset_data,
166
	},
167
	{
168
		.callback	= mbp_dmi_match,
169
		.ident		= "MacBookAir 1,1",
170
		.matches	= {
171
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
172
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir1,1"),
173
		},
174
		.driver_data	= (void *)&intel_chipset_data,
175
	},
176
	{
177
		.callback	= mbp_dmi_match,
178
		.ident		= "MacBook 5,1",
179
		.matches	= {
180
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
181
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,1"),
182
		},
183
		.driver_data	= (void *)&nvidia_chipset_data,
184
	},
185
	{
186
		.callback	= mbp_dmi_match,
187
		.ident		= "MacBook 5,2",
188
		.matches	= {
189
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
190
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,2"),
191
		},
192
		.driver_data	= (void *)&nvidia_chipset_data,
193
	},
194
	{
195
		.callback	= mbp_dmi_match,
196
		.ident		= "MacBookAir 2,1",
197
		.matches	= {
198
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
199
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2,1"),
200
		},
201
		.driver_data	= (void *)&nvidia_chipset_data,
202
	},
203
	{
204
		.callback	= mbp_dmi_match,
205
		.ident		= "MacBookPro 5,1",
206
		.matches	= {
207
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
208
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,1"),
209
		},
210
		.driver_data	= (void *)&nvidia_chipset_data,
211
	},
212
	{
213
		.callback	= mbp_dmi_match,
214
		.ident		= "MacBookPro 5,2",
215
		.matches	= {
216
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
217
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,2"),
218
		},
219
		.driver_data	= (void *)&nvidia_chipset_data,
220
	},
221
	{
222
		.callback	= mbp_dmi_match,
223
		.ident		= "MacBookPro 5,5",
224
		.matches	= {
225
			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
226
			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,5"),
227
		},
228
		.driver_data	= (void *)&nvidia_chipset_data,
229
	},
230
	{ }
231
};
232
233
static int __init mbp_init(void)
234
{
235
	if (!dmi_check_system(mbp_device_table))
236
		return -ENODEV;
237
238
	if (!request_region(driver_data->iostart, driver_data->iolen, 
239
						"Macbook Pro backlight"))
240
		return -ENXIO;
241
242
	mbp_backlight_device = backlight_device_register("mbp_backlight",
243
					NULL, NULL, &driver_data->backlight_ops);
244
	if (IS_ERR(mbp_backlight_device)) {
245
		release_region(driver_data->iostart, driver_data->iolen);
246
		return PTR_ERR(mbp_backlight_device);
247
	}
248
249
	mbp_backlight_device->props.max_brightness = 15;
250
	mbp_backlight_device->props.brightness =
251
		driver_data->backlight_ops.get_brightness(mbp_backlight_device);
252
	backlight_update_status(mbp_backlight_device);
253
254
	return 0;
255
}
256
257
static void __exit mbp_exit(void)
258
{
259
	backlight_device_unregister(mbp_backlight_device);
260
261
	release_region(driver_data->iostart, driver_data->iolen);
262
}
263
264
module_init(mbp_init);
265
module_exit(mbp_exit);
266
267
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
268
MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver");
269
MODULE_LICENSE("GPL");
270
MODULE_DEVICE_TABLE(dmi, mbp_device_table);