aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/chrome')
-rw-r--r--drivers/platform/chrome/Kconfig28
-rw-r--r--drivers/platform/chrome/Makefile2
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c408
3 files changed, 438 insertions, 0 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
new file mode 100644
index 000000000000..b13303e75a34
--- /dev/null
+++ b/drivers/platform/chrome/Kconfig
@@ -0,0 +1,28 @@
1#
2# Platform support for Chrome OS hardware (Chromebooks and Chromeboxes)
3#
4
5menuconfig CHROME_PLATFORMS
6 bool "Platform support for Chrome hardware"
7 depends on X86
8 ---help---
9 Say Y here to get to see options for platform support for
10 various Chromebooks and Chromeboxes. This option alone does
11 not add any kernel code.
12
13 If you say N, all options in this submenu will be skipped and disabled.
14
15if CHROME_PLATFORMS
16
17config CHROMEOS_LAPTOP
18 tristate "Chrome OS Laptop"
19 depends on I2C
20 depends on DMI
21 ---help---
22 This driver instantiates i2c and smbus devices such as
23 light sensors and touchpads.
24
25 If you have a supported Chromebook, choose Y or M here.
26 The module will be called chromeos_laptop.
27
28endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
new file mode 100644
index 000000000000..015e9195e226
--- /dev/null
+++ b/drivers/platform/chrome/Makefile
@@ -0,0 +1,2 @@
1
2obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
new file mode 100644
index 000000000000..3e5b4497a1d0
--- /dev/null
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -0,0 +1,408 @@
1/*
2 * chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices.
3 *
4 * Author : Benson Leung <bleung@chromium.org>
5 *
6 * Copyright (C) 2012 Google, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <linux/dmi.h>
25#include <linux/i2c.h>
26#include <linux/i2c/atmel_mxt_ts.h>
27#include <linux/input.h>
28#include <linux/interrupt.h>
29#include <linux/module.h>
30
31#define ATMEL_TP_I2C_ADDR 0x4b
32#define ATMEL_TP_I2C_BL_ADDR 0x25
33#define ATMEL_TS_I2C_ADDR 0x4a
34#define ATMEL_TS_I2C_BL_ADDR 0x26
35#define CYAPA_TP_I2C_ADDR 0x67
36#define ISL_ALS_I2C_ADDR 0x44
37#define TAOS_ALS_I2C_ADDR 0x29
38
39static struct i2c_client *als;
40static struct i2c_client *tp;
41static struct i2c_client *ts;
42
43const char *i2c_adapter_names[] = {
44 "SMBus I801 adapter",
45 "i915 gmbus vga",
46 "i915 gmbus panel",
47};
48
49/* Keep this enum consistent with i2c_adapter_names */
50enum i2c_adapter_type {
51 I2C_ADAPTER_SMBUS = 0,
52 I2C_ADAPTER_VGADDC,
53 I2C_ADAPTER_PANEL,
54};
55
56static struct i2c_board_info __initdata cyapa_device = {
57 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
58 .flags = I2C_CLIENT_WAKE,
59};
60
61static struct i2c_board_info __initdata isl_als_device = {
62 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
63};
64
65static struct i2c_board_info __initdata tsl2583_als_device = {
66 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
67};
68
69static struct i2c_board_info __initdata tsl2563_als_device = {
70 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
71};
72
73static struct mxt_platform_data atmel_224s_tp_platform_data = {
74 .x_line = 18,
75 .y_line = 12,
76 .x_size = 102*20,
77 .y_size = 68*20,
78 .blen = 0x80, /* Gain setting is in upper 4 bits */
79 .threshold = 0x32,
80 .voltage = 0, /* 3.3V */
81 .orient = MXT_VERTICAL_FLIP,
82 .irqflags = IRQF_TRIGGER_FALLING,
83 .is_tp = true,
84 .key_map = { KEY_RESERVED,
85 KEY_RESERVED,
86 KEY_RESERVED,
87 BTN_LEFT },
88 .config = NULL,
89 .config_length = 0,
90};
91
92static struct i2c_board_info __initdata atmel_224s_tp_device = {
93 I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
94 .platform_data = &atmel_224s_tp_platform_data,
95 .flags = I2C_CLIENT_WAKE,
96};
97
98static struct mxt_platform_data atmel_1664s_platform_data = {
99 .x_line = 32,
100 .y_line = 50,
101 .x_size = 1700,
102 .y_size = 2560,
103 .blen = 0x89, /* Gain setting is in upper 4 bits */
104 .threshold = 0x28,
105 .voltage = 0, /* 3.3V */
106 .orient = MXT_ROTATED_90_COUNTER,
107 .irqflags = IRQF_TRIGGER_FALLING,
108 .is_tp = false,
109 .config = NULL,
110 .config_length = 0,
111};
112
113static struct i2c_board_info __initdata atmel_1664s_device = {
114 I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
115 .platform_data = &atmel_1664s_platform_data,
116 .flags = I2C_CLIENT_WAKE,
117};
118
119static struct i2c_client __init *__add_probed_i2c_device(
120 const char *name,
121 int bus,
122 struct i2c_board_info *info,
123 const unsigned short *addrs)
124{
125 const struct dmi_device *dmi_dev;
126 const struct dmi_dev_onboard *dev_data;
127 struct i2c_adapter *adapter;
128 struct i2c_client *client;
129
130 if (bus < 0)
131 return NULL;
132 /*
133 * If a name is specified, look for irq platform information stashed
134 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
135 */
136 if (name) {
137 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
138 if (!dmi_dev) {
139 pr_err("%s failed to dmi find device %s.\n",
140 __func__,
141 name);
142 return NULL;
143 }
144 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
145 if (!dev_data) {
146 pr_err("%s failed to get data from dmi for %s.\n",
147 __func__, name);
148 return NULL;
149 }
150 info->irq = dev_data->instance;
151 }
152
153 adapter = i2c_get_adapter(bus);
154 if (!adapter) {
155 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
156 return NULL;
157 }
158
159 /* add the i2c device */
160 client = i2c_new_probed_device(adapter, info, addrs, NULL);
161 if (!client)
162 pr_err("%s failed to register device %d-%02x\n",
163 __func__, bus, info->addr);
164 else
165 pr_debug("%s added i2c device %d-%02x\n",
166 __func__, bus, info->addr);
167
168 i2c_put_adapter(adapter);
169 return client;
170}
171
172static int __init __find_i2c_adap(struct device *dev, void *data)
173{
174 const char *name = data;
175 static const char *prefix = "i2c-";
176 struct i2c_adapter *adapter;
177 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
178 return 0;
179 adapter = to_i2c_adapter(dev);
180 return (strncmp(adapter->name, name, strlen(name)) == 0);
181}
182
183static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
184{
185 struct device *dev = NULL;
186 struct i2c_adapter *adapter;
187 const char *name = i2c_adapter_names[type];
188 /* find the adapter by name */
189 dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
190 __find_i2c_adap);
191 if (!dev) {
192 pr_err("%s: i2c adapter %s not found on system.\n", __func__,
193 name);
194 return -ENODEV;
195 }
196 adapter = to_i2c_adapter(dev);
197 return adapter->nr;
198}
199
200/*
201 * Takes a list of addresses in addrs as such :
202 * { addr1, ... , addrn, I2C_CLIENT_END };
203 * add_probed_i2c_device will use i2c_new_probed_device
204 * and probe for devices at all of the addresses listed.
205 * Returns NULL if no devices found.
206 * See Documentation/i2c/instantiating-devices for more information.
207 */
208static __init struct i2c_client *add_probed_i2c_device(
209 const char *name,
210 enum i2c_adapter_type type,
211 struct i2c_board_info *info,
212 const unsigned short *addrs)
213{
214 return __add_probed_i2c_device(name,
215 find_i2c_adapter_num(type),
216 info,
217 addrs);
218}
219
220/*
221 * Probes for a device at a single address, the one provided by
222 * info->addr.
223 * Returns NULL if no device found.
224 */
225static __init struct i2c_client *add_i2c_device(const char *name,
226 enum i2c_adapter_type type,
227 struct i2c_board_info *info)
228{
229 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
230 return __add_probed_i2c_device(name,
231 find_i2c_adapter_num(type),
232 info,
233 addr_list);
234}
235
236
237static struct i2c_client __init *add_smbus_device(const char *name,
238 struct i2c_board_info *info)
239{
240 return add_i2c_device(name, I2C_ADAPTER_SMBUS, info);
241}
242
243static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
244{
245 /* add cyapa touchpad on smbus */
246 tp = add_smbus_device("trackpad", &cyapa_device);
247 return 0;
248}
249
250static int __init setup_atmel_224s_tp(const struct dmi_system_id *id)
251{
252 const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
253 ATMEL_TP_I2C_ADDR,
254 I2C_CLIENT_END };
255
256 /* add atmel mxt touchpad on VGA DDC GMBus */
257 tp = add_probed_i2c_device("trackpad", I2C_ADAPTER_VGADDC,
258 &atmel_224s_tp_device, addr_list);
259 return 0;
260}
261
262static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id)
263{
264 const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
265 ATMEL_TS_I2C_ADDR,
266 I2C_CLIENT_END };
267
268 /* add atmel mxt touch device on PANEL GMBus */
269 ts = add_probed_i2c_device("touchscreen", I2C_ADAPTER_PANEL,
270 &atmel_1664s_device, addr_list);
271 return 0;
272}
273
274
275static int __init setup_isl29018_als(const struct dmi_system_id *id)
276{
277 /* add isl29018 light sensor */
278 als = add_smbus_device("lightsensor", &isl_als_device);
279 return 0;
280}
281
282static int __init setup_isl29023_als(const struct dmi_system_id *id)
283{
284 /* add isl29023 light sensor on Panel GMBus */
285 als = add_i2c_device("lightsensor", I2C_ADAPTER_PANEL,
286 &isl_als_device);
287 return 0;
288}
289
290static int __init setup_tsl2583_als(const struct dmi_system_id *id)
291{
292 /* add tsl2583 light sensor on smbus */
293 als = add_smbus_device(NULL, &tsl2583_als_device);
294 return 0;
295}
296
297static int __init setup_tsl2563_als(const struct dmi_system_id *id)
298{
299 /* add tsl2563 light sensor on smbus */
300 als = add_smbus_device(NULL, &tsl2563_als_device);
301 return 0;
302}
303
304static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
305 {
306 .ident = "Samsung Series 5 550 - Touchpad",
307 .matches = {
308 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
309 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
310 },
311 .callback = setup_cyapa_smbus_tp,
312 },
313 {
314 .ident = "Chromebook Pixel - Touchscreen",
315 .matches = {
316 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
317 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
318 },
319 .callback = setup_atmel_1664s_ts,
320 },
321 {
322 .ident = "Chromebook Pixel - Touchpad",
323 .matches = {
324 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
325 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
326 },
327 .callback = setup_atmel_224s_tp,
328 },
329 {
330 .ident = "Samsung Series 5 550 - Light Sensor",
331 .matches = {
332 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
333 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
334 },
335 .callback = setup_isl29018_als,
336 },
337 {
338 .ident = "Chromebook Pixel - Light Sensor",
339 .matches = {
340 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
341 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
342 },
343 .callback = setup_isl29023_als,
344 },
345 {
346 .ident = "Acer C7 Chromebook - Touchpad",
347 .matches = {
348 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
349 },
350 .callback = setup_cyapa_smbus_tp,
351 },
352 {
353 .ident = "HP Pavilion 14 Chromebook - Touchpad",
354 .matches = {
355 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
356 },
357 .callback = setup_cyapa_smbus_tp,
358 },
359 {
360 .ident = "Samsung Series 5 - Light Sensor",
361 .matches = {
362 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
363 },
364 .callback = setup_tsl2583_als,
365 },
366 {
367 .ident = "Cr-48 - Light Sensor",
368 .matches = {
369 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
370 },
371 .callback = setup_tsl2563_als,
372 },
373 {
374 .ident = "Acer AC700 - Light Sensor",
375 .matches = {
376 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
377 },
378 .callback = setup_tsl2563_als,
379 },
380 { }
381};
382MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
383
384static int __init chromeos_laptop_init(void)
385{
386 if (!dmi_check_system(chromeos_laptop_dmi_table)) {
387 pr_debug("%s unsupported system.\n", __func__);
388 return -ENODEV;
389 }
390 return 0;
391}
392
393static void __exit chromeos_laptop_exit(void)
394{
395 if (als)
396 i2c_unregister_device(als);
397 if (tp)
398 i2c_unregister_device(tp);
399 if (ts)
400 i2c_unregister_device(ts);
401}
402
403module_init(chromeos_laptop_init);
404module_exit(chromeos_laptop_exit);
405
406MODULE_DESCRIPTION("Chrome OS Laptop driver");
407MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
408MODULE_LICENSE("GPL");