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