]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/platform/chrome/chromeos_laptop.c
2559a0407c58b15d635bea86fc819062ee303d96
[karo-tx-linux.git] / drivers / platform / chrome / chromeos_laptop.c
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 #include <linux/platform_device.h>
31
32 #define ATMEL_TP_I2C_ADDR       0x4b
33 #define ATMEL_TP_I2C_BL_ADDR    0x25
34 #define ATMEL_TS_I2C_ADDR       0x4a
35 #define ATMEL_TS_I2C_BL_ADDR    0x26
36 #define CYAPA_TP_I2C_ADDR       0x67
37 #define ISL_ALS_I2C_ADDR        0x44
38 #define TAOS_ALS_I2C_ADDR       0x29
39
40 static struct i2c_client *als;
41 static struct i2c_client *tp;
42 static struct i2c_client *ts;
43
44 static const char *i2c_adapter_names[] = {
45         "SMBus I801 adapter",
46         "i915 gmbus vga",
47         "i915 gmbus panel",
48 };
49
50 /* Keep this enum consistent with i2c_adapter_names */
51 enum i2c_adapter_type {
52         I2C_ADAPTER_SMBUS = 0,
53         I2C_ADAPTER_VGADDC,
54         I2C_ADAPTER_PANEL,
55 };
56
57 struct i2c_peripheral {
58         int (*add)(enum i2c_adapter_type type);
59         enum i2c_adapter_type type;
60 };
61
62 #define MAX_I2C_PERIPHERALS 3
63
64 struct chromeos_laptop {
65         struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
66 };
67
68 static struct chromeos_laptop *cros_laptop;
69
70 static struct i2c_board_info cyapa_device = {
71         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
72         .flags          = I2C_CLIENT_WAKE,
73 };
74
75 static struct i2c_board_info isl_als_device = {
76         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
77 };
78
79 static struct i2c_board_info tsl2583_als_device = {
80         I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
81 };
82
83 static struct i2c_board_info tsl2563_als_device = {
84         I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
85 };
86
87 static struct mxt_platform_data atmel_224s_tp_platform_data = {
88         .x_size                 = 102*20,
89         .y_size                 = 68*20,
90         .orient                 = MXT_VERTICAL_FLIP,
91         .irqflags               = IRQF_TRIGGER_FALLING,
92         .is_tp                  = true,
93         .key_map                = { KEY_RESERVED,
94                                     KEY_RESERVED,
95                                     KEY_RESERVED,
96                                     BTN_LEFT },
97         .config                 = NULL,
98         .config_length          = 0,
99 };
100
101 static struct i2c_board_info atmel_224s_tp_device = {
102         I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
103         .platform_data = &atmel_224s_tp_platform_data,
104         .flags          = I2C_CLIENT_WAKE,
105 };
106
107 static struct mxt_platform_data atmel_1664s_platform_data = {
108         .x_size                 = 1700,
109         .y_size                 = 2560,
110         .orient                 = MXT_ROTATED_90_COUNTER,
111         .irqflags               = IRQF_TRIGGER_FALLING,
112         .is_tp                  = false,
113         .config                 = NULL,
114         .config_length          = 0,
115 };
116
117 static struct i2c_board_info atmel_1664s_device = {
118         I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
119         .platform_data = &atmel_1664s_platform_data,
120         .flags          = I2C_CLIENT_WAKE,
121 };
122
123 static struct i2c_client *__add_probed_i2c_device(
124                 const char *name,
125                 int bus,
126                 struct i2c_board_info *info,
127                 const unsigned short *addrs)
128 {
129         const struct dmi_device *dmi_dev;
130         const struct dmi_dev_onboard *dev_data;
131         struct i2c_adapter *adapter;
132         struct i2c_client *client;
133
134         if (bus < 0)
135                 return NULL;
136         /*
137          * If a name is specified, look for irq platform information stashed
138          * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
139          */
140         if (name) {
141                 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
142                 if (!dmi_dev) {
143                         pr_err("%s failed to dmi find device %s.\n",
144                                __func__,
145                                name);
146                         return NULL;
147                 }
148                 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
149                 if (!dev_data) {
150                         pr_err("%s failed to get data from dmi for %s.\n",
151                                __func__, name);
152                         return NULL;
153                 }
154                 info->irq = dev_data->instance;
155         }
156
157         adapter = i2c_get_adapter(bus);
158         if (!adapter) {
159                 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
160                 return NULL;
161         }
162
163         /* add the i2c device */
164         client = i2c_new_probed_device(adapter, info, addrs, NULL);
165         if (!client)
166                 pr_err("%s failed to register device %d-%02x\n",
167                        __func__, bus, info->addr);
168         else
169                 pr_debug("%s added i2c device %d-%02x\n",
170                          __func__, bus, info->addr);
171
172         i2c_put_adapter(adapter);
173         return client;
174 }
175
176 static int __find_i2c_adap(struct device *dev, void *data)
177 {
178         const char *name = data;
179         static const char *prefix = "i2c-";
180         struct i2c_adapter *adapter;
181         if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
182                 return 0;
183         adapter = to_i2c_adapter(dev);
184         return (strncmp(adapter->name, name, strlen(name)) == 0);
185 }
186
187 static int find_i2c_adapter_num(enum i2c_adapter_type type)
188 {
189         struct device *dev = NULL;
190         struct i2c_adapter *adapter;
191         const char *name = i2c_adapter_names[type];
192         /* find the adapter by name */
193         dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
194                               __find_i2c_adap);
195         if (!dev) {
196                 /* Adapters may appear later. Deferred probing will retry */
197                 pr_notice("%s: i2c adapter %s not found on system.\n", __func__,
198                           name);
199                 return -ENODEV;
200         }
201         adapter = to_i2c_adapter(dev);
202         return adapter->nr;
203 }
204
205 /*
206  * Takes a list of addresses in addrs as such :
207  * { addr1, ... , addrn, I2C_CLIENT_END };
208  * add_probed_i2c_device will use i2c_new_probed_device
209  * and probe for devices at all of the addresses listed.
210  * Returns NULL if no devices found.
211  * See Documentation/i2c/instantiating-devices for more information.
212  */
213 static struct i2c_client *add_probed_i2c_device(
214                 const char *name,
215                 enum i2c_adapter_type type,
216                 struct i2c_board_info *info,
217                 const unsigned short *addrs)
218 {
219         return __add_probed_i2c_device(name,
220                                        find_i2c_adapter_num(type),
221                                        info,
222                                        addrs);
223 }
224
225 /*
226  * Probes for a device at a single address, the one provided by
227  * info->addr.
228  * Returns NULL if no device found.
229  */
230 static struct i2c_client *add_i2c_device(const char *name,
231                                                 enum i2c_adapter_type type,
232                                                 struct i2c_board_info *info)
233 {
234         const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
235         return __add_probed_i2c_device(name,
236                                        find_i2c_adapter_num(type),
237                                        info,
238                                        addr_list);
239 }
240
241 static int setup_cyapa_tp(enum i2c_adapter_type type)
242 {
243         if (tp)
244                 return 0;
245
246         /* add cyapa touchpad */
247         tp = add_i2c_device("trackpad", type, &cyapa_device);
248         return (!tp) ? -EAGAIN : 0;
249 }
250
251 static int setup_atmel_224s_tp(enum i2c_adapter_type type)
252 {
253         const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
254                                              ATMEL_TP_I2C_ADDR,
255                                              I2C_CLIENT_END };
256         if (tp)
257                 return 0;
258
259         /* add atmel mxt touchpad */
260         tp = add_probed_i2c_device("trackpad", type,
261                                    &atmel_224s_tp_device, addr_list);
262         return (!tp) ? -EAGAIN : 0;
263 }
264
265 static int setup_atmel_1664s_ts(enum i2c_adapter_type type)
266 {
267         const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
268                                              ATMEL_TS_I2C_ADDR,
269                                              I2C_CLIENT_END };
270         if (ts)
271                 return 0;
272
273         /* add atmel mxt touch device */
274         ts = add_probed_i2c_device("touchscreen", type,
275                                    &atmel_1664s_device, addr_list);
276         return (!ts) ? -EAGAIN : 0;
277 }
278
279 static int setup_isl29018_als(enum i2c_adapter_type type)
280 {
281         if (als)
282                 return 0;
283
284         /* add isl29018 light sensor */
285         als = add_i2c_device("lightsensor", type, &isl_als_device);
286         return (!als) ? -EAGAIN : 0;
287 }
288
289 static int setup_tsl2583_als(enum i2c_adapter_type type)
290 {
291         if (als)
292                 return 0;
293
294         /* add tsl2583 light sensor */
295         als = add_i2c_device(NULL, type, &tsl2583_als_device);
296         return (!als) ? -EAGAIN : 0;
297 }
298
299 static int setup_tsl2563_als(enum i2c_adapter_type type)
300 {
301         if (als)
302                 return 0;
303
304         /* add tsl2563 light sensor */
305         als = add_i2c_device(NULL, type, &tsl2563_als_device);
306         return (!als) ? -EAGAIN : 0;
307 }
308
309 static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id)
310 {
311         cros_laptop = (void *)id->driver_data;
312         pr_debug("DMI Matched %s.\n", id->ident);
313
314         /* Indicate to dmi_scan that processing is done. */
315         return 1;
316 }
317
318 static int chromeos_laptop_probe(struct platform_device *pdev)
319 {
320         int i;
321         int ret = 0;
322
323         for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
324                 struct i2c_peripheral *i2c_dev;
325
326                 i2c_dev = &cros_laptop->i2c_peripherals[i];
327
328                 /* No more peripherals. */
329                 if (i2c_dev->add == NULL)
330                         break;
331
332                 /* Add the device. Set -EPROBE_DEFER on any failure */
333                 if (i2c_dev->add(i2c_dev->type))
334                         ret = -EPROBE_DEFER;
335         }
336
337         return ret;
338 }
339
340 static struct chromeos_laptop samsung_series_5_550 = {
341         .i2c_peripherals = {
342                 /* Touchpad. */
343                 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
344                 /* Light Sensor. */
345                 { .add = setup_isl29018_als, I2C_ADAPTER_SMBUS },
346         },
347 };
348
349 static struct chromeos_laptop samsung_series_5 = {
350         .i2c_peripherals = {
351                 /* Light Sensor. */
352                 { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS },
353         },
354 };
355
356 static struct chromeos_laptop chromebook_pixel = {
357         .i2c_peripherals = {
358                 /* Touch Screen. */
359                 { .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL },
360                 /* Touchpad. */
361                 { .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC },
362                 /* Light Sensor. */
363                 { .add = setup_isl29018_als, I2C_ADAPTER_PANEL },
364         },
365 };
366
367 static struct chromeos_laptop acer_c7_chromebook = {
368         .i2c_peripherals = {
369                 /* Touchpad. */
370                 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
371         },
372 };
373
374 static struct chromeos_laptop acer_ac700 = {
375         .i2c_peripherals = {
376                 /* Light Sensor. */
377                 { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
378         },
379 };
380
381 static struct chromeos_laptop hp_pavilion_14_chromebook = {
382         .i2c_peripherals = {
383                 /* Touchpad. */
384                 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
385         },
386 };
387
388 static struct chromeos_laptop cr48 = {
389         .i2c_peripherals = {
390                 /* Light Sensor. */
391                 { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
392         },
393 };
394
395 #define _CBDD(board_) \
396         .callback = chromeos_laptop_dmi_matched, \
397         .driver_data = (void *)&board_
398
399 static struct dmi_system_id chromeos_laptop_dmi_table[] __initdata = {
400         {
401                 .ident = "Samsung Series 5 550",
402                 .matches = {
403                         DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
404                         DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
405                 },
406                 _CBDD(samsung_series_5_550),
407         },
408         {
409                 .ident = "Samsung Series 5",
410                 .matches = {
411                         DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
412                 },
413                 _CBDD(samsung_series_5),
414         },
415         {
416                 .ident = "Chromebook Pixel",
417                 .matches = {
418                         DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
419                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
420                 },
421                 _CBDD(chromebook_pixel),
422         },
423         {
424                 .ident = "Acer C7 Chromebook",
425                 .matches = {
426                         DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
427                 },
428                 _CBDD(acer_c7_chromebook),
429         },
430         {
431                 .ident = "Acer AC700",
432                 .matches = {
433                         DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
434                 },
435                 _CBDD(acer_ac700),
436         },
437         {
438                 .ident = "HP Pavilion 14 Chromebook",
439                 .matches = {
440                         DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
441                 },
442                 _CBDD(hp_pavilion_14_chromebook),
443         },
444         {
445                 .ident = "Cr-48",
446                 .matches = {
447                         DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
448                 },
449                 _CBDD(cr48),
450         },
451         { }
452 };
453 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
454
455 static struct platform_device *cros_platform_device;
456
457 static struct platform_driver cros_platform_driver = {
458         .driver = {
459                 .name = "chromeos_laptop",
460                 .owner = THIS_MODULE,
461         },
462         .probe = chromeos_laptop_probe,
463 };
464
465 static int __init chromeos_laptop_init(void)
466 {
467         int ret;
468         if (!dmi_check_system(chromeos_laptop_dmi_table)) {
469                 pr_debug("%s unsupported system.\n", __func__);
470                 return -ENODEV;
471         }
472
473         ret = platform_driver_register(&cros_platform_driver);
474         if (ret)
475                 return ret;
476
477         cros_platform_device = platform_device_alloc("chromeos_laptop", -1);
478         if (!cros_platform_device) {
479                 ret = -ENOMEM;
480                 goto fail_platform_device1;
481         }
482
483         ret = platform_device_add(cros_platform_device);
484         if (ret)
485                 goto fail_platform_device2;
486
487         return 0;
488
489 fail_platform_device2:
490         platform_device_put(cros_platform_device);
491 fail_platform_device1:
492         platform_driver_unregister(&cros_platform_driver);
493         return ret;
494 }
495
496 static void __exit chromeos_laptop_exit(void)
497 {
498         if (als)
499                 i2c_unregister_device(als);
500         if (tp)
501                 i2c_unregister_device(tp);
502         if (ts)
503                 i2c_unregister_device(ts);
504
505         platform_device_unregister(cros_platform_device);
506         platform_driver_unregister(&cros_platform_driver);
507 }
508
509 module_init(chromeos_laptop_init);
510 module_exit(chromeos_laptop_exit);
511
512 MODULE_DESCRIPTION("Chrome OS Laptop driver");
513 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
514 MODULE_LICENSE("GPL");