Merge remote-tracking branches 'asoc/fix/adsp', 'asoc/fix/arizona', 'asoc/fix/atmel...
[linux-drm-fsl-dcu.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
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
39 static struct i2c_client *als;
40 static struct i2c_client *tp;
41 static struct i2c_client *ts;
42
43 const 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 */
50 enum i2c_adapter_type {
51         I2C_ADAPTER_SMBUS = 0,
52         I2C_ADAPTER_VGADDC,
53         I2C_ADAPTER_PANEL,
54 };
55
56 static struct i2c_board_info __initdata cyapa_device = {
57         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
58         .flags          = I2C_CLIENT_WAKE,
59 };
60
61 static struct i2c_board_info __initdata isl_als_device = {
62         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
63 };
64
65 static struct i2c_board_info __initdata tsl2583_als_device = {
66         I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
67 };
68
69 static struct i2c_board_info __initdata tsl2563_als_device = {
70         I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
71 };
72
73 static 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
92 static 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
98 static 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
113 static 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
119 static 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
172 static 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
183 static 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  */
208 static __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  */
225 static __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
237 static 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
243 static 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
250 static 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
262 static 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
275 static 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
282 static 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
290 static 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
297 static 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
304 static 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 };
382 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
383
384 static 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
393 static 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
403 module_init(chromeos_laptop_init);
404 module_exit(chromeos_laptop_exit);
405
406 MODULE_DESCRIPTION("Chrome OS Laptop driver");
407 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
408 MODULE_LICENSE("GPL");