blob: 5c69cfd97ff09a3c6eae0ad79db5c09b89332e16 [file] [log] [blame]
Benson Leungd1381f42012-10-25 14:21:21 -07001/*
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>
Benson Leung2ef39202013-03-07 19:43:34 -080026#include <linux/i2c/atmel_mxt_ts.h>
27#include <linux/input.h>
28#include <linux/interrupt.h>
Benson Leungd1381f42012-10-25 14:21:21 -070029#include <linux/module.h>
30
Benson Leung8e1ad4c2013-02-21 12:14:59 -080031#define ATMEL_TP_I2C_ADDR 0x4b
32#define ATMEL_TP_I2C_BL_ADDR 0x25
Yufeng Shen33a84f82013-02-21 12:15:00 -080033#define ATMEL_TS_I2C_ADDR 0x4a
34#define ATMEL_TS_I2C_BL_ADDR 0x26
Benson Leungd1381f42012-10-25 14:21:21 -070035#define CYAPA_TP_I2C_ADDR 0x67
36#define ISL_ALS_I2C_ADDR 0x44
Benson Leungaabf3f42013-02-01 14:34:45 -080037#define TAOS_ALS_I2C_ADDR 0x29
Benson Leungd1381f42012-10-25 14:21:21 -070038
39static struct i2c_client *als;
40static struct i2c_client *tp;
Yufeng Shen33a84f82013-02-21 12:15:00 -080041static struct i2c_client *ts;
Benson Leungd1381f42012-10-25 14:21:21 -070042
43const char *i2c_adapter_names[] = {
44 "SMBus I801 adapter",
Benson Leung741bf0c2013-02-21 12:14:55 -080045 "i915 gmbus vga",
46 "i915 gmbus panel",
Benson Leungd1381f42012-10-25 14:21:21 -070047};
48
49/* Keep this enum consistent with i2c_adapter_names */
50enum i2c_adapter_type {
51 I2C_ADAPTER_SMBUS = 0,
Benson Leung741bf0c2013-02-21 12:14:55 -080052 I2C_ADAPTER_VGADDC,
53 I2C_ADAPTER_PANEL,
Benson Leungd1381f42012-10-25 14:21:21 -070054};
55
Aaron Durbinec199dd2013-10-20 20:58:24 -070056struct i2c_peripheral {
57 void (*add)(enum i2c_adapter_type type);
58 enum i2c_adapter_type type;
59};
60
61#define MAX_I2C_PERIPHERALS 3
62
63struct chromeos_laptop {
64 struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
65};
66
Benson Leungd1381f42012-10-25 14:21:21 -070067static struct i2c_board_info __initdata cyapa_device = {
68 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
69 .flags = I2C_CLIENT_WAKE,
70};
71
72static struct i2c_board_info __initdata isl_als_device = {
73 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
74};
75
Benson Leung8016bcb2013-02-01 14:34:46 -080076static struct i2c_board_info __initdata tsl2583_als_device = {
77 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
78};
79
Benson Leungaabf3f42013-02-01 14:34:45 -080080static struct i2c_board_info __initdata tsl2563_als_device = {
81 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
82};
83
Benson Leung2ef39202013-03-07 19:43:34 -080084static struct mxt_platform_data atmel_224s_tp_platform_data = {
85 .x_line = 18,
86 .y_line = 12,
87 .x_size = 102*20,
88 .y_size = 68*20,
89 .blen = 0x80, /* Gain setting is in upper 4 bits */
90 .threshold = 0x32,
91 .voltage = 0, /* 3.3V */
92 .orient = MXT_VERTICAL_FLIP,
93 .irqflags = IRQF_TRIGGER_FALLING,
94 .is_tp = true,
95 .key_map = { KEY_RESERVED,
96 KEY_RESERVED,
97 KEY_RESERVED,
98 BTN_LEFT },
99 .config = NULL,
100 .config_length = 0,
101};
102
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800103static struct i2c_board_info __initdata atmel_224s_tp_device = {
104 I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
Benson Leung2ef39202013-03-07 19:43:34 -0800105 .platform_data = &atmel_224s_tp_platform_data,
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800106 .flags = I2C_CLIENT_WAKE,
107};
108
Benson Leung2ef39202013-03-07 19:43:34 -0800109static struct mxt_platform_data atmel_1664s_platform_data = {
110 .x_line = 32,
111 .y_line = 50,
112 .x_size = 1700,
113 .y_size = 2560,
114 .blen = 0x89, /* Gain setting is in upper 4 bits */
115 .threshold = 0x28,
116 .voltage = 0, /* 3.3V */
117 .orient = MXT_ROTATED_90_COUNTER,
118 .irqflags = IRQF_TRIGGER_FALLING,
119 .is_tp = false,
120 .config = NULL,
121 .config_length = 0,
122};
123
Yufeng Shen33a84f82013-02-21 12:15:00 -0800124static struct i2c_board_info __initdata atmel_1664s_device = {
125 I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
Benson Leung2ef39202013-03-07 19:43:34 -0800126 .platform_data = &atmel_1664s_platform_data,
Yufeng Shen33a84f82013-02-21 12:15:00 -0800127 .flags = I2C_CLIENT_WAKE,
128};
129
Benson Leungd1381f42012-10-25 14:21:21 -0700130static struct i2c_client __init *__add_probed_i2c_device(
131 const char *name,
132 int bus,
133 struct i2c_board_info *info,
134 const unsigned short *addrs)
135{
136 const struct dmi_device *dmi_dev;
137 const struct dmi_dev_onboard *dev_data;
138 struct i2c_adapter *adapter;
139 struct i2c_client *client;
140
141 if (bus < 0)
142 return NULL;
143 /*
144 * If a name is specified, look for irq platform information stashed
145 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
146 */
147 if (name) {
148 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
149 if (!dmi_dev) {
150 pr_err("%s failed to dmi find device %s.\n",
151 __func__,
152 name);
153 return NULL;
154 }
155 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
156 if (!dev_data) {
157 pr_err("%s failed to get data from dmi for %s.\n",
158 __func__, name);
159 return NULL;
160 }
161 info->irq = dev_data->instance;
162 }
163
164 adapter = i2c_get_adapter(bus);
165 if (!adapter) {
166 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
167 return NULL;
168 }
169
170 /* add the i2c device */
171 client = i2c_new_probed_device(adapter, info, addrs, NULL);
172 if (!client)
173 pr_err("%s failed to register device %d-%02x\n",
174 __func__, bus, info->addr);
175 else
176 pr_debug("%s added i2c device %d-%02x\n",
177 __func__, bus, info->addr);
178
179 i2c_put_adapter(adapter);
180 return client;
181}
182
183static int __init __find_i2c_adap(struct device *dev, void *data)
184{
185 const char *name = data;
186 static const char *prefix = "i2c-";
187 struct i2c_adapter *adapter;
188 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
189 return 0;
190 adapter = to_i2c_adapter(dev);
191 return (strncmp(adapter->name, name, strlen(name)) == 0);
192}
193
194static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
195{
196 struct device *dev = NULL;
197 struct i2c_adapter *adapter;
198 const char *name = i2c_adapter_names[type];
199 /* find the adapter by name */
200 dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
201 __find_i2c_adap);
202 if (!dev) {
203 pr_err("%s: i2c adapter %s not found on system.\n", __func__,
204 name);
205 return -ENODEV;
206 }
207 adapter = to_i2c_adapter(dev);
208 return adapter->nr;
209}
210
211/*
Benson Leungbcaf0892013-02-21 12:14:58 -0800212 * Takes a list of addresses in addrs as such :
213 * { addr1, ... , addrn, I2C_CLIENT_END };
214 * add_probed_i2c_device will use i2c_new_probed_device
215 * and probe for devices at all of the addresses listed.
216 * Returns NULL if no devices found.
217 * See Documentation/i2c/instantiating-devices for more information.
218 */
219static __init struct i2c_client *add_probed_i2c_device(
220 const char *name,
221 enum i2c_adapter_type type,
222 struct i2c_board_info *info,
223 const unsigned short *addrs)
224{
225 return __add_probed_i2c_device(name,
226 find_i2c_adapter_num(type),
227 info,
228 addrs);
229}
230
231/*
Benson Leungd1381f42012-10-25 14:21:21 -0700232 * Probes for a device at a single address, the one provided by
233 * info->addr.
234 * Returns NULL if no device found.
235 */
Benson Leunge7b28842013-02-21 12:14:56 -0800236static __init struct i2c_client *add_i2c_device(const char *name,
237 enum i2c_adapter_type type,
238 struct i2c_board_info *info)
Benson Leungd1381f42012-10-25 14:21:21 -0700239{
240 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
241 return __add_probed_i2c_device(name,
Benson Leunge7b28842013-02-21 12:14:56 -0800242 find_i2c_adapter_num(type),
Benson Leungd1381f42012-10-25 14:21:21 -0700243 info,
244 addr_list);
245}
246
Aaron Durbinec199dd2013-10-20 20:58:24 -0700247static int __init setup_cyapa_tp(enum i2c_adapter_type type)
Benson Leunge7b28842013-02-21 12:14:56 -0800248{
Aaron Durbinec199dd2013-10-20 20:58:24 -0700249 /* add cyapa touchpad */
250 tp = add_i2c_device("trackpad", type, &cyapa_device);
Benson Leungd1381f42012-10-25 14:21:21 -0700251 return 0;
252}
253
Aaron Durbinec199dd2013-10-20 20:58:24 -0700254static int __init setup_atmel_224s_tp(enum i2c_adapter_type type)
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800255{
256 const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
257 ATMEL_TP_I2C_ADDR,
258 I2C_CLIENT_END };
259
Aaron Durbinec199dd2013-10-20 20:58:24 -0700260 /* add atmel mxt touchpad */
261 tp = add_probed_i2c_device("trackpad", type,
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800262 &atmel_224s_tp_device, addr_list);
263 return 0;
264}
265
Aaron Durbinec199dd2013-10-20 20:58:24 -0700266static int __init setup_atmel_1664s_ts(enum i2c_adapter_type type)
Yufeng Shen33a84f82013-02-21 12:15:00 -0800267{
268 const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
269 ATMEL_TS_I2C_ADDR,
270 I2C_CLIENT_END };
271
Aaron Durbinec199dd2013-10-20 20:58:24 -0700272 /* add atmel mxt touch device */
273 ts = add_probed_i2c_device("touchscreen", type,
Yufeng Shen33a84f82013-02-21 12:15:00 -0800274 &atmel_1664s_device, addr_list);
275 return 0;
276}
277
Aaron Durbinec199dd2013-10-20 20:58:24 -0700278static int __init setup_isl29018_als(enum i2c_adapter_type type)
Benson Leungd1381f42012-10-25 14:21:21 -0700279{
280 /* add isl29018 light sensor */
Aaron Durbinec199dd2013-10-20 20:58:24 -0700281 als = add_i2c_device("lightsensor", type, &isl_als_device);
Benson Leungd1381f42012-10-25 14:21:21 -0700282 return 0;
283}
284
Aaron Durbinec199dd2013-10-20 20:58:24 -0700285static int __init setup_tsl2583_als(enum i2c_adapter_type type)
Benson Leungcc5c3982013-02-21 12:14:57 -0800286{
Aaron Durbinec199dd2013-10-20 20:58:24 -0700287 /* add tsl2583 light sensor */
288 als = add_i2c_device(NULL, type, &tsl2583_als_device);
Benson Leungcc5c3982013-02-21 12:14:57 -0800289 return 0;
290}
291
Aaron Durbinec199dd2013-10-20 20:58:24 -0700292static int __init setup_tsl2563_als(enum i2c_adapter_type type)
Benson Leung8016bcb2013-02-01 14:34:46 -0800293{
Aaron Durbinec199dd2013-10-20 20:58:24 -0700294 /* add tsl2563 light sensor */
295 als = add_i2c_device(NULL, type, &tsl2563_als_device);
Benson Leung8016bcb2013-02-01 14:34:46 -0800296 return 0;
297}
298
Aaron Durbinec199dd2013-10-20 20:58:24 -0700299static int __init
300chromeos_laptop_add_peripherals(const struct dmi_system_id *id)
Benson Leungaabf3f42013-02-01 14:34:45 -0800301{
Aaron Durbinec199dd2013-10-20 20:58:24 -0700302 int i;
303 struct chromeos_laptop *cros_laptop = (void *)id->driver_data;
304
305 pr_debug("Adding peripherals for %s.\n", id->ident);
306
307 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
308 struct i2c_peripheral *i2c_dev;
309
310 i2c_dev = &cros_laptop->i2c_peripherals[i];
311
312 /* No more peripherals. */
313 if (i2c_dev->add == NULL)
314 break;
315
316 /* Add the device. */
317 i2c_dev->add(i2c_dev->type);
318 }
319
320 /* Indicate to dmi_scan that processing is done. */
321 return 1;
Benson Leungaabf3f42013-02-01 14:34:45 -0800322}
323
Aaron Durbinec199dd2013-10-20 20:58:24 -0700324static struct chromeos_laptop samsung_series_5_550 __initdata = {
325 .i2c_peripherals = {
326 /* Touchpad. */
327 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
328 /* Light Sensor. */
329 { .add = setup_isl29018_als, I2C_ADAPTER_SMBUS },
330 },
331};
332
333static struct chromeos_laptop samsung_series_5 __initdata = {
334 .i2c_peripherals = {
335 /* Light Sensor. */
336 { .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS },
337 },
338};
339
340static struct chromeos_laptop chromebook_pixel __initdata = {
341 .i2c_peripherals = {
342 /* Touch Screen. */
343 { .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL },
344 /* Touchpad. */
345 { .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC },
346 /* Light Sensor. */
347 { .add = setup_isl29018_als, I2C_ADAPTER_PANEL },
348 },
349};
350
351static struct chromeos_laptop acer_c7_chromebook __initdata = {
352 .i2c_peripherals = {
353 /* Touchpad. */
354 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
355 },
356};
357
358static struct chromeos_laptop acer_ac700 __initdata = {
359 .i2c_peripherals = {
360 /* Light Sensor. */
361 { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
362 },
363};
364
365static struct chromeos_laptop hp_pavilion_14_chromebook __initdata = {
366 .i2c_peripherals = {
367 /* Touchpad. */
368 { .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
369 },
370};
371
372static struct chromeos_laptop cr48 __initdata = {
373 .i2c_peripherals = {
374 /* Light Sensor. */
375 { .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
376 },
377};
378
379#define _CBDD(board_) \
380 .callback = &chromeos_laptop_add_peripherals, \
381 .driver_data = (void *)&board_
382
Benson Leungd1381f42012-10-25 14:21:21 -0700383static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
384 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700385 .ident = "Samsung Series 5 550",
Benson Leungd1381f42012-10-25 14:21:21 -0700386 .matches = {
387 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
388 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
389 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700390 _CBDD(samsung_series_5_550),
Benson Leungd1381f42012-10-25 14:21:21 -0700391 },
392 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700393 .ident = "Samsung Series 5",
Benson Leung8016bcb2013-02-01 14:34:46 -0800394 .matches = {
395 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
396 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700397 _CBDD(samsung_series_5),
Benson Leung8016bcb2013-02-01 14:34:46 -0800398 },
399 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700400 .ident = "Chromebook Pixel",
Benson Leungaabf3f42013-02-01 14:34:45 -0800401 .matches = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700402 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
403 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
Benson Leungaabf3f42013-02-01 14:34:45 -0800404 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700405 _CBDD(chromebook_pixel),
Benson Leungaabf3f42013-02-01 14:34:45 -0800406 },
407 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700408 .ident = "Acer C7 Chromebook",
409 .matches = {
410 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
411 },
412 _CBDD(acer_c7_chromebook),
413 },
414 {
415 .ident = "Acer AC700",
Benson Leungaabf3f42013-02-01 14:34:45 -0800416 .matches = {
417 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
418 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700419 _CBDD(acer_ac700),
420 },
421 {
422 .ident = "HP Pavilion 14 Chromebook",
423 .matches = {
424 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
425 },
426 _CBDD(hp_pavilion_14_chromebook),
427 },
428 {
429 .ident = "Cr-48",
430 .matches = {
431 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
432 },
433 _CBDD(cr48),
Benson Leungaabf3f42013-02-01 14:34:45 -0800434 },
Benson Leungd1381f42012-10-25 14:21:21 -0700435 { }
436};
437MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
438
439static int __init chromeos_laptop_init(void)
440{
441 if (!dmi_check_system(chromeos_laptop_dmi_table)) {
442 pr_debug("%s unsupported system.\n", __func__);
443 return -ENODEV;
444 }
445 return 0;
446}
447
448static void __exit chromeos_laptop_exit(void)
449{
450 if (als)
451 i2c_unregister_device(als);
452 if (tp)
453 i2c_unregister_device(tp);
Yufeng Shen33a84f82013-02-21 12:15:00 -0800454 if (ts)
455 i2c_unregister_device(ts);
Benson Leungd1381f42012-10-25 14:21:21 -0700456}
457
458module_init(chromeos_laptop_init);
459module_exit(chromeos_laptop_exit);
460
461MODULE_DESCRIPTION("Chrome OS Laptop driver");
462MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
463MODULE_LICENSE("GPL");