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