CHROMIUM: chromeos_laptop: Add MODULE_DEVICE_TABLE
[cascardo/linux.git] / drivers / platform / x86 / chromeos_laptop.c
1 /*
2  *  chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus and platform
3  *                      devices.
4  *
5  *  Copyright (C) 2012 Google, Inc.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include <linux/dmi.h>
24 #include <linux/i2c.h>
25 #include <linux/i2c/atmel_mxt_ts.h>
26 #include <linux/interrupt.h>
27 #include <linux/module.h>
28 #include <linux/platform_device.h>
29
30 #define ATMEL_TP_I2C_ADDR       0x4b
31 #define ATMEL_TP_I2C_BL_ADDR    0x25
32 #define ATMEL_TS_I2C_ADDR       0x4a
33 #define ATMEL_TS_I2C_BL_ADDR    0x26
34 #define CYAPA_TP_I2C_ADDR       0x67
35 #define ISL_ALS_I2C_ADDR        0x44
36 #define TAOS_ALS_I2C_ADDR       0x29
37
38 static struct i2c_client *als;
39 static struct i2c_client *tp;
40 static struct i2c_client *ts;
41
42 const char *i2c_adapter_names[] = {
43         "SMBus I801 adapter",
44         "i915 gmbus vga",
45         "i915 gmbus panel",
46 };
47
48 /* Keep this enum consistent with i2c_adapter_names */
49 enum i2c_adapter_type {
50         I2C_ADAPTER_SMBUS = 0,
51         I2C_ADAPTER_VGADDC,
52         I2C_ADAPTER_PANEL,
53 };
54
55 static struct i2c_board_info __initdata cyapa_device = {
56         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
57         .flags          = I2C_CLIENT_WAKE,
58 };
59
60 static struct i2c_board_info __initdata isl_als_device = {
61         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
62 };
63
64 static struct i2c_board_info __initdata tsl2583_als_device = {
65         I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
66 };
67
68 static struct i2c_board_info __initdata tsl2563_als_device = {
69         I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
70 };
71
72 static const u8 atmel_224e_tp_config_data[] = {
73         /* MXT_GEN_COMMAND(6) */
74         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75         /* MXT_GEN_POWERCONFIG(7) */
76         0xff, 0xff, 0x32,
77         /* MXT_GEN_ACQUIRE(8) */
78         0x06, 0x00, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79         /* MXT_TOUCH_MULTI(9) */
80         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
81         0x00, 0x02, 0x01, 0x00, 0x0a, 0x03, 0x03, 0x0a, 0x00, 0x00,
82         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83         0x20, 0x00, 0x37, 0x37, 0x00,
84         /* MXT_TOUCH_KEYARRAY(15) */
85         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86         0x00,
87         /* MXT_SPT_COMMSCONFIG(18) */
88         0x00, 0x00,
89         /* MXT_SPT_GPIOPWM(19) */
90         0x03, 0xDF, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92         /* MXT_TOUCH_PROXIMITY(23) */
93         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94         0x00, 0x00, 0x00, 0x00, 0x00,
95         /* MXT_SPT_SELFTEST(25)  */
96         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97         0x00, 0x00, 0x00, 0x00,
98         /* MXT_PROCI_GRIPSUPPRESSION(40) */
99         0x00, 0x00, 0x00, 0x00, 0x00,
100         /* MXT_PROCI_TOUCHSUPPRESSION(42)  */
101         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102         /* MXT_SPT_CTECONFIG(46) */
103         0x00, 0x02, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
104         /* MXT_PROCI_STYLUS(47) */
105         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106         /* MXT_PROCG_NOISESUPPRESSION(48) */
107         0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112         0x00, 0x00, 0x00, 0x00
113 };
114
115 static struct mxt_platform_data atmel_224e_tp_platform_data = {
116         .x_line                 = 18,
117         .y_line                 = 12,
118         .x_size                 = 102*20,
119         .y_size                 = 68*20,
120         .blen                   = 0x20, /* Gain setting is in upper 4 bits */
121         .threshold              = 0x19,
122         .voltage                = 0,    /* 3.3V */
123         .orient                 = MXT_HORIZONTAL_FLIP,
124         .irqflags               = IRQF_TRIGGER_FALLING,
125         .config                 = atmel_224e_tp_config_data,
126         .config_length          = sizeof(atmel_224e_tp_config_data),
127 };
128
129 static struct i2c_board_info __initdata atmel_224e_tp_device = {
130         I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
131         .platform_data = &atmel_224e_tp_platform_data,
132         .flags          = I2C_CLIENT_WAKE,
133 };
134
135 static struct i2c_board_info __initdata atmel_224s_tp_device = {
136         I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
137         .platform_data = NULL,
138         .flags          = I2C_CLIENT_WAKE,
139 };
140
141 static struct i2c_board_info __initdata atmel_1664s_device = {
142         I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
143         .platform_data = NULL,
144         .flags          = I2C_CLIENT_WAKE,
145 };
146
147 static __init struct i2c_client *__add_probed_i2c_device(
148                 const char *name,
149                 int bus,
150                 struct i2c_board_info *info,
151                 const unsigned short *addrs)
152 {
153         const struct dmi_device *dmi_dev;
154         const struct dmi_dev_onboard *dev_data;
155         struct i2c_adapter *adapter;
156         struct i2c_client *client;
157
158         if (bus < 0)
159                 return NULL;
160         /*
161          * If a name is specified, look for irq platform information stashed
162          * in DMI_DEV_TYPE_DEV_ONBOARD.
163          */
164         if (name) {
165                 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
166                 if (!dmi_dev) {
167                         pr_err("%s failed to dmi find device %s.\n",
168                                __func__,
169                                name);
170                         return NULL;
171                 }
172                 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
173                 if (!dev_data) {
174                         pr_err("%s failed to get data from dmi for %s.\n",
175                                __func__, name);
176                         return NULL;
177                 }
178                 info->irq = dev_data->instance;
179         }
180
181         adapter = i2c_get_adapter(bus);
182         if (!adapter) {
183                 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
184                 return NULL;
185         }
186
187         /* add the i2c device */
188         client = i2c_new_probed_device(adapter, info, addrs, NULL);
189         if (!client)
190                 pr_err("%s failed to register device %d-%02x\n",
191                        __func__, bus, info->addr);
192         else
193                 pr_debug("%s added i2c device %d-%02x\n",
194                          __func__, bus, info->addr);
195
196         i2c_put_adapter(adapter);
197         return client;
198 }
199
200 static int __init __find_i2c_adap(struct device *dev, void *data)
201 {
202         const char *name = data;
203         const char *prefix = "i2c-";
204         struct i2c_adapter *adapter;
205         if (strncmp(dev_name(dev), prefix, strlen(prefix)))
206                 return 0;
207         adapter = to_i2c_adapter(dev);
208         return !strncmp(adapter->name, name, strlen(name));
209 }
210
211 static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
212 {
213         struct device *dev = NULL;
214         struct i2c_adapter *adapter;
215         const char *name = i2c_adapter_names[type];
216         /* find the adapter by name */
217         dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
218                               __find_i2c_adap);
219         if (!dev) {
220                 pr_err("%s: i2c adapter %s not found on system.\n", __func__,
221                        name);
222                 return -ENODEV;
223         }
224         adapter = to_i2c_adapter(dev);
225         return adapter->nr;
226 }
227
228 /*
229  * Takes a list of addresses in addrs as such :
230  * { addr1, ... , addrn, I2C_CLIENT_END };
231  * chromeos_laptop_add_probed_i2c_device will use i2c_new_probed_device
232  * and probe for devices at all of the addresses listed.
233  * Returns NULL if no devices found.
234  * See Documentation/i2c/instantiating-devices for more information.
235  */
236 static __init struct i2c_client *chromeos_laptop_add_probed_i2c_device(
237                 const char *name,
238                 enum i2c_adapter_type type,
239                 struct i2c_board_info *info,
240                 const unsigned short *addrs)
241 {
242         return __add_probed_i2c_device(name,
243                                        find_i2c_adapter_num(type),
244                                        info,
245                                        addrs);
246 }
247
248 /*
249  * Probes for a device at a single address, the one provided by
250  * info->addr.
251  * Returns NULL if no device found.
252  */
253 static __init struct i2c_client *chromeos_laptop_add_i2c_device(
254                 const char *name,
255                 enum i2c_adapter_type type,
256                 struct i2c_board_info *info)
257 {
258         const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
259         return __add_probed_i2c_device(name,
260                                        find_i2c_adapter_num(type),
261                                        info,
262                                        addr_list);
263 }
264
265 static __init struct i2c_client *add_smbus_device(const char *name,
266                                                   struct i2c_board_info *info)
267 {
268         return chromeos_laptop_add_i2c_device(name, I2C_ADAPTER_SMBUS, info);
269 }
270
271 static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id)
272 {
273         const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
274                                              ATMEL_TS_I2C_ADDR,
275                                              I2C_CLIENT_END };
276
277         ts = chromeos_laptop_add_probed_i2c_device("touchscreen",
278                                                    I2C_ADAPTER_PANEL,
279                                                    &atmel_1664s_device,
280                                                    addr_list);
281         return 0;
282 }
283
284 static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
285 {
286         /* add cyapa touchpad */
287         tp = add_smbus_device("trackpad", &cyapa_device);
288         return 0;
289 }
290
291 static int __init setup_atmel_224s_tp(const struct dmi_system_id *id)
292 {
293         const unsigned short atmel_addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
294                                                    ATMEL_TP_I2C_ADDR,
295                                                    I2C_CLIENT_END };
296
297         /* add atmel mxt touchpad */
298         tp = chromeos_laptop_add_probed_i2c_device("trackpad",
299                                                    I2C_ADAPTER_VGADDC,
300                                                    &atmel_224s_tp_device,
301                                                    atmel_addr_list);
302         return 0;
303 }
304
305 static int __init setup_lumpy_tp(const struct dmi_system_id *id)
306 {
307         /* first try cyapa touchpad on smbus */
308         setup_cyapa_smbus_tp(id);
309         if (tp)
310                 return 0;
311
312         /* then try atmel mxt touchpad */
313         tp = chromeos_laptop_add_i2c_device("trackpad",
314                                             I2C_ADAPTER_VGADDC,
315                                             &atmel_224e_tp_device);
316         return 0;
317 }
318
319 static int __init setup_isl29018_als(const struct dmi_system_id *id)
320 {
321         /* add isl29018 light sensor */
322         als = add_smbus_device("lightsensor", &isl_als_device);
323         return 0;
324 }
325
326 static int __init setup_isl29023_als(const struct dmi_system_id *id)
327 {
328         /* add isl29023 light sensor on Panel DDC GMBus */
329         als = chromeos_laptop_add_i2c_device("lightsensor",
330                                              I2C_ADAPTER_PANEL,
331                                              &isl_als_device);
332         return 0;
333 }
334
335 static int __init setup_tsl2583_als(const struct dmi_system_id *id)
336 {
337         /* add tsl2583 light sensor */
338         als = add_smbus_device(NULL, &tsl2583_als_device);
339         return 0;
340 }
341
342 static int __init setup_tsl2563_als(const struct dmi_system_id *id)
343 {
344         /* add tsl2563 light sensor */
345         als = add_smbus_device(NULL, &tsl2563_als_device);
346         return 0;
347 }
348
349 static struct platform_device *kb_backlight_device;
350
351 static int __init setup_keyboard_backlight(const struct dmi_system_id *id)
352 {
353         kb_backlight_device =
354                 platform_device_register_simple("chromeos-keyboard-leds",
355                                                 -1, NULL, 0);
356         if (IS_ERR(kb_backlight_device)) {
357                 pr_warn("Error registering Chrome OS keyboard LEDs.\n");
358                 kb_backlight_device = NULL;
359         }
360         return 0;
361 }
362
363 static const struct __initdata dmi_system_id chromeos_laptop_dmi_table[] = {
364         {
365                 .ident = "Lumpy - Touchpads",
366                 .matches = {
367                         DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
368                 },
369                 .callback = setup_lumpy_tp,
370         },
371         {
372                 .ident = "Chromebook Pixel - Touchscreen",
373                 .matches = {
374                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
375                 },
376                 .callback = setup_atmel_1664s_ts,
377         },
378         {
379                 .ident = "Chromebook Pixel - Touchpad",
380                 .matches = {
381                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
382                 },
383                 .callback = setup_atmel_224s_tp,
384         },
385         {
386                 .ident = "isl29018 - Light Sensor",
387                 .matches = {
388                         DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
389                 },
390                 .callback = setup_isl29018_als,
391         },
392         {
393                 .ident = "isl29023 - Light Sensor",
394                 .matches = {
395                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
396                 },
397                 .callback = setup_isl29023_als,
398         },
399         {
400                 .ident = "Parrot - Touchpad",
401                 .matches = {
402                         DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
403                 },
404                 .callback = setup_cyapa_smbus_tp,
405         },
406         {
407                 .ident = "Butterfy - Touchpad",
408                 .matches = {
409                         DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
410                 },
411                 .callback = setup_cyapa_smbus_tp,
412         },
413         {
414                 .ident = "tsl2583 - Light Sensor",
415                 .matches = {
416                         DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
417                 },
418                 .callback = setup_tsl2583_als,
419         },
420         {
421                 .ident = "tsl2563 - Light Sensor",
422                 .matches = {
423                         DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
424                 },
425                 .callback = setup_tsl2563_als,
426         },
427         {
428                 .ident = "tsl2563 - Light Sensor",
429                 .matches = {
430                         DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
431                 },
432                 .callback = setup_tsl2563_als,
433         },
434         {
435                 .ident = "Chromebook Pixel - Keyboard backlight",
436                 .matches = {
437                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
438                 },
439                 .callback = setup_keyboard_backlight,
440         },
441         { }
442 };
443 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
444
445 static int __init chromeos_laptop_init(void)
446 {
447         if (!dmi_check_system(chromeos_laptop_dmi_table)) {
448                 pr_debug("%s unsupported system.\n", __func__);
449                 return -ENODEV;
450         }
451         return 0;
452 }
453
454 static void __exit chromeos_laptop_exit(void)
455 {
456         if (als)
457                 i2c_unregister_device(als);
458         if (tp)
459                 i2c_unregister_device(tp);
460         if (ts)
461                 i2c_unregister_device(ts);
462         if (kb_backlight_device)
463                 platform_device_unregister(kb_backlight_device);
464 }
465
466 module_init(chromeos_laptop_init);
467 module_exit(chromeos_laptop_exit);
468
469 MODULE_DESCRIPTION("Chrome OS Laptop driver");
470 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
471 MODULE_LICENSE("GPL");