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