]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/platform/x86/chromeos_laptop.c
Platform: x86: chromeos_laptop - Add isl light sensor for Pixel
[karo-tx-linux.git] / drivers / platform / x86 / 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/module.h>
27
28 #define ATMEL_TP_I2C_ADDR       0x4b
29 #define ATMEL_TP_I2C_BL_ADDR    0x25
30 #define ATMEL_TS_I2C_ADDR       0x4a
31 #define ATMEL_TS_I2C_BL_ADDR    0x26
32 #define CYAPA_TP_I2C_ADDR       0x67
33 #define ISL_ALS_I2C_ADDR        0x44
34 #define TAOS_ALS_I2C_ADDR       0x29
35
36 static struct i2c_client *als;
37 static struct i2c_client *tp;
38 static struct i2c_client *ts;
39
40 const char *i2c_adapter_names[] = {
41         "SMBus I801 adapter",
42 };
43
44 /* Keep this enum consistent with i2c_adapter_names */
45 enum i2c_adapter_type {
46         I2C_ADAPTER_SMBUS = 0,
47 };
48
49 static struct i2c_board_info __initdata cyapa_device = {
50         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
51         .flags          = I2C_CLIENT_WAKE,
52 };
53
54 static struct i2c_board_info __initdata isl_als_device = {
55         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
56 };
57
58 static struct i2c_board_info __initdata tsl2583_als_device = {
59         I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
60 };
61
62 static struct i2c_board_info __initdata tsl2563_als_device = {
63         I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
64 };
65
66 static struct i2c_board_info __initdata atmel_224s_tp_device = {
67         I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
68         .platform_data = NULL,
69         .flags          = I2C_CLIENT_WAKE,
70 };
71
72 static struct i2c_board_info __initdata atmel_1664s_device = {
73         I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
74         .platform_data = NULL,
75         .flags          = I2C_CLIENT_WAKE,
76 };
77
78 static struct i2c_client __init *__add_probed_i2c_device(
79                 const char *name,
80                 int bus,
81                 struct i2c_board_info *info,
82                 const unsigned short *addrs)
83 {
84         const struct dmi_device *dmi_dev;
85         const struct dmi_dev_onboard *dev_data;
86         struct i2c_adapter *adapter;
87         struct i2c_client *client;
88
89         if (bus < 0)
90                 return NULL;
91         /*
92          * If a name is specified, look for irq platform information stashed
93          * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
94          */
95         if (name) {
96                 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
97                 if (!dmi_dev) {
98                         pr_err("%s failed to dmi find device %s.\n",
99                                __func__,
100                                name);
101                         return NULL;
102                 }
103                 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
104                 if (!dev_data) {
105                         pr_err("%s failed to get data from dmi for %s.\n",
106                                __func__, name);
107                         return NULL;
108                 }
109                 info->irq = dev_data->instance;
110         }
111
112         adapter = i2c_get_adapter(bus);
113         if (!adapter) {
114                 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
115                 return NULL;
116         }
117
118         /* add the i2c device */
119         client = i2c_new_probed_device(adapter, info, addrs, NULL);
120         if (!client)
121                 pr_err("%s failed to register device %d-%02x\n",
122                        __func__, bus, info->addr);
123         else
124                 pr_debug("%s added i2c device %d-%02x\n",
125                          __func__, bus, info->addr);
126
127         i2c_put_adapter(adapter);
128         return client;
129 }
130
131 static int __init __find_i2c_adap(struct device *dev, void *data)
132 {
133         const char *name = data;
134         static const char *prefix = "i2c-";
135         struct i2c_adapter *adapter;
136         if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
137                 return 0;
138         adapter = to_i2c_adapter(dev);
139         return (strncmp(adapter->name, name, strlen(name)) == 0);
140 }
141
142 static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
143 {
144         struct device *dev = NULL;
145         struct i2c_adapter *adapter;
146         const char *name = i2c_adapter_names[type];
147         /* find the adapter by name */
148         dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
149                               __find_i2c_adap);
150         if (!dev) {
151                 pr_err("%s: i2c adapter %s not found on system.\n", __func__,
152                        name);
153                 return -ENODEV;
154         }
155         adapter = to_i2c_adapter(dev);
156         return adapter->nr;
157 }
158
159 /*
160  * Takes a list of addresses in addrs as such :
161  * { addr1, ... , addrn, I2C_CLIENT_END };
162  * add_probed_i2c_device will use i2c_new_probed_device
163  * and probe for devices at all of the addresses listed.
164  * Returns NULL if no devices found.
165  * See Documentation/i2c/instantiating-devices for more information.
166  */
167 static __init struct i2c_client *add_probed_i2c_device(
168                 const char *name,
169                 enum i2c_adapter_type type,
170                 struct i2c_board_info *info,
171                 const unsigned short *addrs)
172 {
173         return __add_probed_i2c_device(name,
174                                        find_i2c_adapter_num(type),
175                                        info,
176                                        addrs);
177 }
178
179 /*
180  * Probes for a device at a single address, the one provided by
181  * info->addr.
182  * Returns NULL if no device found.
183  */
184 static __init struct i2c_client *add_i2c_device(const char *name,
185                                                 enum i2c_adapter_type type,
186                                                 struct i2c_board_info *info)
187 {
188         const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
189         return __add_probed_i2c_device(name,
190                                        find_i2c_adapter_num(type),
191                                        info,
192                                        addr_list);
193 }
194
195
196 static struct i2c_client __init *add_smbus_device(const char *name,
197                                                   struct i2c_board_info *info)
198 {
199         return add_i2c_device(name, I2C_ADAPTER_SMBUS, info);
200 }
201
202 static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
203 {
204         /* add cyapa touchpad on smbus */
205         tp = add_smbus_device("trackpad", &cyapa_device);
206         return 0;
207 }
208
209 static int __init setup_atmel_224s_tp(const struct dmi_system_id *id)
210 {
211         const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
212                                              ATMEL_TP_I2C_ADDR,
213                                              I2C_CLIENT_END };
214
215         /* add atmel mxt touchpad on VGA DDC GMBus */
216         tp = add_probed_i2c_device("trackpad", I2C_ADAPTER_VGADDC,
217                                    &atmel_224s_tp_device, addr_list);
218         return 0;
219 }
220
221 static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id)
222 {
223         const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
224                                              ATMEL_TS_I2C_ADDR,
225                                              I2C_CLIENT_END };
226
227         /* add atmel mxt touch device on PANEL GMBus */
228         ts = add_probed_i2c_device("touchscreen", I2C_ADAPTER_PANEL,
229                                    &atmel_1664s_device, addr_list);
230         return 0;
231 }
232
233
234 static int __init setup_isl29018_als(const struct dmi_system_id *id)
235 {
236         /* add isl29018 light sensor */
237         als = add_smbus_device("lightsensor", &isl_als_device);
238         return 0;
239 }
240
241 static int __init setup_isl29023_als(const struct dmi_system_id *id)
242 {
243         /* add isl29023 light sensor on Panel GMBus */
244         als = add_i2c_device("lightsensor", I2C_ADAPTER_PANEL,
245                              &isl_als_device);
246         return 0;
247 }
248
249 static int __init setup_tsl2583_als(const struct dmi_system_id *id)
250 {
251         /* add tsl2583 light sensor on smbus */
252         als = add_smbus_device(NULL, &tsl2583_als_device);
253         return 0;
254 }
255
256 static int __init setup_tsl2563_als(const struct dmi_system_id *id)
257 {
258         /* add tsl2563 light sensor on smbus */
259         als = add_smbus_device(NULL, &tsl2563_als_device);
260         return 0;
261 }
262
263 static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
264         {
265                 .ident = "Samsung Series 5 550 - Touchpad",
266                 .matches = {
267                         DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
268                         DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
269                 },
270                 .callback = setup_cyapa_smbus_tp,
271         },
272         {
273                 .ident = "Chromebook Pixel - Touchscreen",
274                 .matches = {
275                         DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
276                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
277                 },
278                 .callback = setup_atmel_1664s_ts,
279         },
280         {
281                 .ident = "Chromebook Pixel - Touchpad",
282                 .matches = {
283                         DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
284                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
285                 },
286                 .callback = setup_atmel_224s_tp,
287         },
288         {
289                 .ident = "Samsung Series 5 550 - Light Sensor",
290                 .matches = {
291                         DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
292                         DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
293                 },
294                 .callback = setup_isl29018_als,
295         },
296         {
297                 .ident = "Chromebook Pixel - Light Sensor",
298                 .matches = {
299                         DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
300                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
301                 },
302                 .callback = setup_isl29023_als,
303         },
304         {
305                 .ident = "Acer C7 Chromebook - Touchpad",
306                 .matches = {
307                         DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
308                 },
309                 .callback = setup_cyapa_smbus_tp,
310         },
311         {
312                 .ident = "HP Pavilion 14 Chromebook - Touchpad",
313                 .matches = {
314                         DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
315                 },
316                 .callback = setup_cyapa_smbus_tp,
317         },
318         {
319                 .ident = "Samsung Series 5 - Light Sensor",
320                 .matches = {
321                         DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
322                 },
323                 .callback = setup_tsl2583_als,
324         },
325         {
326                 .ident = "Cr-48 - Light Sensor",
327                 .matches = {
328                         DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
329                 },
330                 .callback = setup_tsl2563_als,
331         },
332         {
333                 .ident = "Acer AC700 - Light Sensor",
334                 .matches = {
335                         DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
336                 },
337                 .callback = setup_tsl2563_als,
338         },
339         { }
340 };
341 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
342
343 static int __init chromeos_laptop_init(void)
344 {
345         if (!dmi_check_system(chromeos_laptop_dmi_table)) {
346                 pr_debug("%s unsupported system.\n", __func__);
347                 return -ENODEV;
348         }
349         return 0;
350 }
351
352 static void __exit chromeos_laptop_exit(void)
353 {
354         if (als)
355                 i2c_unregister_device(als);
356         if (tp)
357                 i2c_unregister_device(tp);
358         if (ts)
359                 i2c_unregister_device(ts);
360 }
361
362 module_init(chromeos_laptop_init);
363 module_exit(chromeos_laptop_exit);
364
365 MODULE_DESCRIPTION("Chrome OS Laptop driver");
366 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
367 MODULE_LICENSE("GPL");