1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A 4 * 5 * Copyright 2009-2011 Analog Devices Inc. 6 */ 7 8 #include <linux/device.h> 9 #include <linux/input.h> 10 #include <linux/interrupt.h> 11 #include <linux/slab.h> 12 #include <linux/input/ad714x.h> 13 #include <linux/module.h> 14 #include "ad714x.h" 15 16 #define AD714X_PWR_CTRL 0x0 17 #define AD714X_STG_CAL_EN_REG 0x1 18 #define AD714X_AMB_COMP_CTRL0_REG 0x2 19 #define AD714X_PARTID_REG 0x17 20 #define AD7142_PARTID 0xE620 21 #define AD7143_PARTID 0xE630 22 #define AD7147_PARTID 0x1470 23 #define AD7148_PARTID 0x1480 24 #define AD714X_STAGECFG_REG 0x80 25 #define AD714X_SYSCFG_REG 0x0 26 27 #define STG_LOW_INT_EN_REG 0x5 28 #define STG_HIGH_INT_EN_REG 0x6 29 #define STG_COM_INT_EN_REG 0x7 30 #define STG_LOW_INT_STA_REG 0x8 31 #define STG_HIGH_INT_STA_REG 0x9 32 #define STG_COM_INT_STA_REG 0xA 33 34 #define CDC_RESULT_S0 0xB 35 #define CDC_RESULT_S1 0xC 36 #define CDC_RESULT_S2 0xD 37 #define CDC_RESULT_S3 0xE 38 #define CDC_RESULT_S4 0xF 39 #define CDC_RESULT_S5 0x10 40 #define CDC_RESULT_S6 0x11 41 #define CDC_RESULT_S7 0x12 42 #define CDC_RESULT_S8 0x13 43 #define CDC_RESULT_S9 0x14 44 #define CDC_RESULT_S10 0x15 45 #define CDC_RESULT_S11 0x16 46 47 #define STAGE0_AMBIENT 0xF1 48 #define STAGE1_AMBIENT 0x115 49 #define STAGE2_AMBIENT 0x139 50 #define STAGE3_AMBIENT 0x15D 51 #define STAGE4_AMBIENT 0x181 52 #define STAGE5_AMBIENT 0x1A5 53 #define STAGE6_AMBIENT 0x1C9 54 #define STAGE7_AMBIENT 0x1ED 55 #define STAGE8_AMBIENT 0x211 56 #define STAGE9_AMBIENT 0x234 57 #define STAGE10_AMBIENT 0x259 58 #define STAGE11_AMBIENT 0x27D 59 60 #define PER_STAGE_REG_NUM 36 61 #define STAGE_CFGREG_NUM 8 62 #define SYS_CFGREG_NUM 8 63 64 /* 65 * driver information which will be used to maintain the software flow 66 */ 67 enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE }; 68 69 struct ad714x_slider_drv { 70 int highest_stage; 71 int abs_pos; 72 int flt_pos; 73 enum ad714x_device_state state; 74 struct input_dev *input; 75 }; 76 77 struct ad714x_wheel_drv { 78 int abs_pos; 79 int flt_pos; 80 int pre_highest_stage; 81 int highest_stage; 82 enum ad714x_device_state state; 83 struct input_dev *input; 84 }; 85 86 struct ad714x_touchpad_drv { 87 int x_highest_stage; 88 int x_flt_pos; 89 int x_abs_pos; 90 int y_highest_stage; 91 int y_flt_pos; 92 int y_abs_pos; 93 int left_ep; 94 int left_ep_val; 95 int right_ep; 96 int right_ep_val; 97 int top_ep; 98 int top_ep_val; 99 int bottom_ep; 100 int bottom_ep_val; 101 enum ad714x_device_state state; 102 struct input_dev *input; 103 }; 104 105 struct ad714x_button_drv { 106 enum ad714x_device_state state; 107 /* 108 * Unlike slider/wheel/touchpad, all buttons point to 109 * same input_dev instance 110 */ 111 struct input_dev *input; 112 }; 113 114 struct ad714x_driver_data { 115 struct ad714x_slider_drv *slider; 116 struct ad714x_wheel_drv *wheel; 117 struct ad714x_touchpad_drv *touchpad; 118 struct ad714x_button_drv *button; 119 }; 120 121 /* 122 * information to integrate all things which will be private data 123 * of spi/i2c device 124 */ 125 126 static void ad714x_use_com_int(struct ad714x_chip *ad714x, 127 int start_stage, int end_stage) 128 { 129 unsigned short data; 130 unsigned short mask; 131 132 mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1); 133 134 ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1); 135 data |= 1 << end_stage; 136 ad714x->write(ad714x, STG_COM_INT_EN_REG, data); 137 138 ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1); 139 data &= ~mask; 140 ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data); 141 } 142 143 static void ad714x_use_thr_int(struct ad714x_chip *ad714x, 144 int start_stage, int end_stage) 145 { 146 unsigned short data; 147 unsigned short mask; 148 149 mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1); 150 151 ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1); 152 data &= ~(1 << end_stage); 153 ad714x->write(ad714x, STG_COM_INT_EN_REG, data); 154 155 ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1); 156 data |= mask; 157 ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data); 158 } 159 160 static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x, 161 int start_stage, int end_stage) 162 { 163 int max_res = 0; 164 int max_idx = 0; 165 int i; 166 167 for (i = start_stage; i <= end_stage; i++) { 168 if (ad714x->sensor_val[i] > max_res) { 169 max_res = ad714x->sensor_val[i]; 170 max_idx = i; 171 } 172 } 173 174 return max_idx; 175 } 176 177 static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x, 178 int start_stage, int end_stage, 179 int highest_stage, int max_coord) 180 { 181 int a_param, b_param; 182 183 if (highest_stage == start_stage) { 184 a_param = ad714x->sensor_val[start_stage + 1]; 185 b_param = ad714x->sensor_val[start_stage] + 186 ad714x->sensor_val[start_stage + 1]; 187 } else if (highest_stage == end_stage) { 188 a_param = ad714x->sensor_val[end_stage] * 189 (end_stage - start_stage) + 190 ad714x->sensor_val[end_stage - 1] * 191 (end_stage - start_stage - 1); 192 b_param = ad714x->sensor_val[end_stage] + 193 ad714x->sensor_val[end_stage - 1]; 194 } else { 195 a_param = ad714x->sensor_val[highest_stage] * 196 (highest_stage - start_stage) + 197 ad714x->sensor_val[highest_stage - 1] * 198 (highest_stage - start_stage - 1) + 199 ad714x->sensor_val[highest_stage + 1] * 200 (highest_stage - start_stage + 1); 201 b_param = ad714x->sensor_val[highest_stage] + 202 ad714x->sensor_val[highest_stage - 1] + 203 ad714x->sensor_val[highest_stage + 1]; 204 } 205 206 return (max_coord / (end_stage - start_stage)) * a_param / b_param; 207 } 208 209 /* 210 * One button can connect to multi positive and negative of CDCs 211 * Multi-buttons can connect to same positive/negative of one CDC 212 */ 213 static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx) 214 { 215 struct ad714x_button_plat *hw = &ad714x->hw->button[idx]; 216 struct ad714x_button_drv *sw = &ad714x->sw->button[idx]; 217 218 switch (sw->state) { 219 case IDLE: 220 if (((ad714x->h_state & hw->h_mask) == hw->h_mask) && 221 ((ad714x->l_state & hw->l_mask) == hw->l_mask)) { 222 dev_dbg(ad714x->dev, "button %d touched\n", idx); 223 input_report_key(sw->input, hw->keycode, 1); 224 input_sync(sw->input); 225 sw->state = ACTIVE; 226 } 227 break; 228 229 case ACTIVE: 230 if (((ad714x->h_state & hw->h_mask) != hw->h_mask) || 231 ((ad714x->l_state & hw->l_mask) != hw->l_mask)) { 232 dev_dbg(ad714x->dev, "button %d released\n", idx); 233 input_report_key(sw->input, hw->keycode, 0); 234 input_sync(sw->input); 235 sw->state = IDLE; 236 } 237 break; 238 239 default: 240 break; 241 } 242 } 243 244 /* 245 * The response of a sensor is defined by the absolute number of codes 246 * between the current CDC value and the ambient value. 247 */ 248 static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx) 249 { 250 struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; 251 int i; 252 253 ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage, 254 &ad714x->adc_reg[hw->start_stage], 255 hw->end_stage - hw->start_stage + 1); 256 257 for (i = hw->start_stage; i <= hw->end_stage; i++) { 258 ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, 259 &ad714x->amb_reg[i], 1); 260 261 ad714x->sensor_val[i] = 262 abs(ad714x->adc_reg[i] - ad714x->amb_reg[i]); 263 } 264 } 265 266 static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx) 267 { 268 struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; 269 struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; 270 271 sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage, 272 hw->end_stage); 273 274 dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx, 275 sw->highest_stage); 276 } 277 278 /* 279 * The formulae are very straight forward. It uses the sensor with the 280 * highest response and the 2 adjacent ones. 281 * When Sensor 0 has the highest response, only sensor 0 and sensor 1 282 * are used in the calculations. Similarly when the last sensor has the 283 * highest response, only the last sensor and the second last sensors 284 * are used in the calculations. 285 * 286 * For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1 287 * v += Sensor response(i)*i 288 * w += Sensor response(i) 289 * POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w) 290 */ 291 static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx) 292 { 293 struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; 294 struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; 295 296 sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage, 297 sw->highest_stage, hw->max_coord); 298 299 dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx, 300 sw->abs_pos); 301 } 302 303 /* 304 * To minimise the Impact of the noise on the algorithm, ADI developed a 305 * routine that filters the CDC results after they have been read by the 306 * host processor. 307 * The filter used is an Infinite Input Response(IIR) filter implemented 308 * in firmware and attenuates the noise on the CDC results after they've 309 * been read by the host processor. 310 * Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) + 311 * Latest_CDC_result * Coefficient)/10 312 */ 313 static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx) 314 { 315 struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; 316 317 sw->flt_pos = (sw->flt_pos * (10 - 4) + 318 sw->abs_pos * 4)/10; 319 320 dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx, 321 sw->flt_pos); 322 } 323 324 static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx) 325 { 326 struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; 327 328 ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage); 329 } 330 331 static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx) 332 { 333 struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; 334 335 ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage); 336 } 337 338 static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx) 339 { 340 struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx]; 341 struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx]; 342 unsigned short h_state, c_state; 343 unsigned short mask; 344 345 mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1); 346 347 h_state = ad714x->h_state & mask; 348 c_state = ad714x->c_state & mask; 349 350 switch (sw->state) { 351 case IDLE: 352 if (h_state) { 353 sw->state = JITTER; 354 /* In End of Conversion interrupt mode, the AD714X 355 * continuously generates hardware interrupts. 356 */ 357 ad714x_slider_use_com_int(ad714x, idx); 358 dev_dbg(ad714x->dev, "slider %d touched\n", idx); 359 } 360 break; 361 362 case JITTER: 363 if (c_state == mask) { 364 ad714x_slider_cal_sensor_val(ad714x, idx); 365 ad714x_slider_cal_highest_stage(ad714x, idx); 366 ad714x_slider_cal_abs_pos(ad714x, idx); 367 sw->flt_pos = sw->abs_pos; 368 sw->state = ACTIVE; 369 } 370 break; 371 372 case ACTIVE: 373 if (c_state == mask) { 374 if (h_state) { 375 ad714x_slider_cal_sensor_val(ad714x, idx); 376 ad714x_slider_cal_highest_stage(ad714x, idx); 377 ad714x_slider_cal_abs_pos(ad714x, idx); 378 ad714x_slider_cal_flt_pos(ad714x, idx); 379 input_report_abs(sw->input, ABS_X, sw->flt_pos); 380 input_report_key(sw->input, BTN_TOUCH, 1); 381 } else { 382 /* When the user lifts off the sensor, configure 383 * the AD714X back to threshold interrupt mode. 384 */ 385 ad714x_slider_use_thr_int(ad714x, idx); 386 sw->state = IDLE; 387 input_report_key(sw->input, BTN_TOUCH, 0); 388 dev_dbg(ad714x->dev, "slider %d released\n", 389 idx); 390 } 391 input_sync(sw->input); 392 } 393 break; 394 395 default: 396 break; 397 } 398 } 399 400 /* 401 * When the scroll wheel is activated, we compute the absolute position based 402 * on the sensor values. To calculate the position, we first determine the 403 * sensor that has the greatest response among the 8 sensors that constitutes 404 * the scrollwheel. Then we determined the 2 sensors on either sides of the 405 * sensor with the highest response and we apply weights to these sensors. 406 */ 407 static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx) 408 { 409 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 410 struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; 411 412 sw->pre_highest_stage = sw->highest_stage; 413 sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage, 414 hw->end_stage); 415 416 dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx, 417 sw->highest_stage); 418 } 419 420 static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx) 421 { 422 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 423 int i; 424 425 ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage, 426 &ad714x->adc_reg[hw->start_stage], 427 hw->end_stage - hw->start_stage + 1); 428 429 for (i = hw->start_stage; i <= hw->end_stage; i++) { 430 ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, 431 &ad714x->amb_reg[i], 1); 432 if (ad714x->adc_reg[i] > ad714x->amb_reg[i]) 433 ad714x->sensor_val[i] = 434 ad714x->adc_reg[i] - ad714x->amb_reg[i]; 435 else 436 ad714x->sensor_val[i] = 0; 437 } 438 } 439 440 /* 441 * When the scroll wheel is activated, we compute the absolute position based 442 * on the sensor values. To calculate the position, we first determine the 443 * sensor that has the greatest response among the sensors that constitutes 444 * the scrollwheel. Then we determined the sensors on either sides of the 445 * sensor with the highest response and we apply weights to these sensors. The 446 * result of this computation gives us the mean value. 447 */ 448 449 static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx) 450 { 451 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 452 struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; 453 int stage_num = hw->end_stage - hw->start_stage + 1; 454 int first_before, highest, first_after; 455 int a_param, b_param; 456 457 first_before = (sw->highest_stage + stage_num - 1) % stage_num; 458 highest = sw->highest_stage; 459 first_after = (sw->highest_stage + stage_num + 1) % stage_num; 460 461 a_param = ad714x->sensor_val[highest] * 462 (highest - hw->start_stage) + 463 ad714x->sensor_val[first_before] * 464 (highest - hw->start_stage - 1) + 465 ad714x->sensor_val[first_after] * 466 (highest - hw->start_stage + 1); 467 b_param = ad714x->sensor_val[highest] + 468 ad714x->sensor_val[first_before] + 469 ad714x->sensor_val[first_after]; 470 471 sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) * 472 a_param) / b_param; 473 474 if (sw->abs_pos > hw->max_coord) 475 sw->abs_pos = hw->max_coord; 476 else if (sw->abs_pos < 0) 477 sw->abs_pos = 0; 478 } 479 480 static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx) 481 { 482 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 483 struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; 484 if (((sw->pre_highest_stage == hw->end_stage) && 485 (sw->highest_stage == hw->start_stage)) || 486 ((sw->pre_highest_stage == hw->start_stage) && 487 (sw->highest_stage == hw->end_stage))) 488 sw->flt_pos = sw->abs_pos; 489 else 490 sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100; 491 492 if (sw->flt_pos > hw->max_coord) 493 sw->flt_pos = hw->max_coord; 494 } 495 496 static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx) 497 { 498 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 499 500 ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage); 501 } 502 503 static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx) 504 { 505 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 506 507 ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage); 508 } 509 510 static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx) 511 { 512 struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; 513 struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; 514 unsigned short h_state, c_state; 515 unsigned short mask; 516 517 mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1); 518 519 h_state = ad714x->h_state & mask; 520 c_state = ad714x->c_state & mask; 521 522 switch (sw->state) { 523 case IDLE: 524 if (h_state) { 525 sw->state = JITTER; 526 /* In End of Conversion interrupt mode, the AD714X 527 * continuously generates hardware interrupts. 528 */ 529 ad714x_wheel_use_com_int(ad714x, idx); 530 dev_dbg(ad714x->dev, "wheel %d touched\n", idx); 531 } 532 break; 533 534 case JITTER: 535 if (c_state == mask) { 536 ad714x_wheel_cal_sensor_val(ad714x, idx); 537 ad714x_wheel_cal_highest_stage(ad714x, idx); 538 ad714x_wheel_cal_abs_pos(ad714x, idx); 539 sw->flt_pos = sw->abs_pos; 540 sw->state = ACTIVE; 541 } 542 break; 543 544 case ACTIVE: 545 if (c_state == mask) { 546 if (h_state) { 547 ad714x_wheel_cal_sensor_val(ad714x, idx); 548 ad714x_wheel_cal_highest_stage(ad714x, idx); 549 ad714x_wheel_cal_abs_pos(ad714x, idx); 550 ad714x_wheel_cal_flt_pos(ad714x, idx); 551 input_report_abs(sw->input, ABS_WHEEL, 552 sw->flt_pos); 553 input_report_key(sw->input, BTN_TOUCH, 1); 554 } else { 555 /* When the user lifts off the sensor, configure 556 * the AD714X back to threshold interrupt mode. 557 */ 558 ad714x_wheel_use_thr_int(ad714x, idx); 559 sw->state = IDLE; 560 input_report_key(sw->input, BTN_TOUCH, 0); 561 562 dev_dbg(ad714x->dev, "wheel %d released\n", 563 idx); 564 } 565 input_sync(sw->input); 566 } 567 break; 568 569 default: 570 break; 571 } 572 } 573 574 static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx) 575 { 576 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 577 int i; 578 579 ad714x->read(ad714x, CDC_RESULT_S0 + hw->x_start_stage, 580 &ad714x->adc_reg[hw->x_start_stage], 581 hw->x_end_stage - hw->x_start_stage + 1); 582 583 for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) { 584 ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM, 585 &ad714x->amb_reg[i], 1); 586 if (ad714x->adc_reg[i] > ad714x->amb_reg[i]) 587 ad714x->sensor_val[i] = 588 ad714x->adc_reg[i] - ad714x->amb_reg[i]; 589 else 590 ad714x->sensor_val[i] = 0; 591 } 592 } 593 594 static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx) 595 { 596 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 597 struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; 598 599 sw->x_highest_stage = ad714x_cal_highest_stage(ad714x, 600 hw->x_start_stage, hw->x_end_stage); 601 sw->y_highest_stage = ad714x_cal_highest_stage(ad714x, 602 hw->y_start_stage, hw->y_end_stage); 603 604 dev_dbg(ad714x->dev, 605 "touchpad %d x_highest_stage:%d, y_highest_stage:%d\n", 606 idx, sw->x_highest_stage, sw->y_highest_stage); 607 } 608 609 /* 610 * If 2 fingers are touching the sensor then 2 peaks can be observed in the 611 * distribution. 612 * The arithmetic doesn't support to get absolute coordinates for multi-touch 613 * yet. 614 */ 615 static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx) 616 { 617 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 618 struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; 619 int i; 620 621 for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) { 622 if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1]) 623 > (ad714x->sensor_val[i + 1] / 10)) 624 return 1; 625 } 626 627 for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) { 628 if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i]) 629 > (ad714x->sensor_val[i] / 10)) 630 return 1; 631 } 632 633 for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) { 634 if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1]) 635 > (ad714x->sensor_val[i + 1] / 10)) 636 return 1; 637 } 638 639 for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) { 640 if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i]) 641 > (ad714x->sensor_val[i] / 10)) 642 return 1; 643 } 644 645 return 0; 646 } 647 648 /* 649 * If only one finger is used to activate the touch pad then only 1 peak will be 650 * registered in the distribution. This peak and the 2 adjacent sensors will be 651 * used in the calculation of the absolute position. This will prevent hand 652 * shadows to affect the absolute position calculation. 653 */ 654 static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx) 655 { 656 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 657 struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; 658 659 sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage, 660 hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord); 661 sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage, 662 hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord); 663 664 dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx, 665 sw->x_abs_pos, sw->y_abs_pos); 666 } 667 668 static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx) 669 { 670 struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; 671 672 sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) + 673 sw->x_abs_pos * 4)/10; 674 sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) + 675 sw->y_abs_pos * 4)/10; 676 677 dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n", 678 idx, sw->x_flt_pos, sw->y_flt_pos); 679 } 680 681 /* 682 * To prevent distortion from showing in the absolute position, it is 683 * necessary to detect the end points. When endpoints are detected, the 684 * driver stops updating the status variables with absolute positions. 685 * End points are detected on the 4 edges of the touchpad sensor. The 686 * method to detect them is the same for all 4. 687 * To detect the end points, the firmware computes the difference in 688 * percent between the sensor on the edge and the adjacent one. The 689 * difference is calculated in percent in order to make the end point 690 * detection independent of the pressure. 691 */ 692 693 #define LEFT_END_POINT_DETECTION_LEVEL 550 694 #define RIGHT_END_POINT_DETECTION_LEVEL 750 695 #define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL 850 696 #define TOP_END_POINT_DETECTION_LEVEL 550 697 #define BOTTOM_END_POINT_DETECTION_LEVEL 950 698 #define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL 700 699 static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx) 700 { 701 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 702 struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; 703 int percent_sensor_diff; 704 705 /* left endpoint detect */ 706 percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] - 707 ad714x->sensor_val[hw->x_start_stage + 1]) * 100 / 708 ad714x->sensor_val[hw->x_start_stage + 1]; 709 if (!sw->left_ep) { 710 if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL) { 711 sw->left_ep = 1; 712 sw->left_ep_val = 713 ad714x->sensor_val[hw->x_start_stage + 1]; 714 } 715 } else { 716 if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) && 717 (ad714x->sensor_val[hw->x_start_stage + 1] > 718 LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val)) 719 sw->left_ep = 0; 720 } 721 722 /* right endpoint detect */ 723 percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] - 724 ad714x->sensor_val[hw->x_end_stage - 1]) * 100 / 725 ad714x->sensor_val[hw->x_end_stage - 1]; 726 if (!sw->right_ep) { 727 if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL) { 728 sw->right_ep = 1; 729 sw->right_ep_val = 730 ad714x->sensor_val[hw->x_end_stage - 1]; 731 } 732 } else { 733 if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) && 734 (ad714x->sensor_val[hw->x_end_stage - 1] > 735 LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val)) 736 sw->right_ep = 0; 737 } 738 739 /* top endpoint detect */ 740 percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] - 741 ad714x->sensor_val[hw->y_start_stage + 1]) * 100 / 742 ad714x->sensor_val[hw->y_start_stage + 1]; 743 if (!sw->top_ep) { 744 if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL) { 745 sw->top_ep = 1; 746 sw->top_ep_val = 747 ad714x->sensor_val[hw->y_start_stage + 1]; 748 } 749 } else { 750 if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) && 751 (ad714x->sensor_val[hw->y_start_stage + 1] > 752 TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val)) 753 sw->top_ep = 0; 754 } 755 756 /* bottom endpoint detect */ 757 percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] - 758 ad714x->sensor_val[hw->y_end_stage - 1]) * 100 / 759 ad714x->sensor_val[hw->y_end_stage - 1]; 760 if (!sw->bottom_ep) { 761 if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL) { 762 sw->bottom_ep = 1; 763 sw->bottom_ep_val = 764 ad714x->sensor_val[hw->y_end_stage - 1]; 765 } 766 } else { 767 if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) && 768 (ad714x->sensor_val[hw->y_end_stage - 1] > 769 TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val)) 770 sw->bottom_ep = 0; 771 } 772 773 return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep; 774 } 775 776 static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx) 777 { 778 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 779 780 ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage); 781 } 782 783 static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx) 784 { 785 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 786 787 ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage); 788 ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage); 789 } 790 791 static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx) 792 { 793 struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx]; 794 struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx]; 795 unsigned short h_state, c_state; 796 unsigned short mask; 797 798 mask = (((1 << (hw->x_end_stage + 1)) - 1) - 799 ((1 << hw->x_start_stage) - 1)) + 800 (((1 << (hw->y_end_stage + 1)) - 1) - 801 ((1 << hw->y_start_stage) - 1)); 802 803 h_state = ad714x->h_state & mask; 804 c_state = ad714x->c_state & mask; 805 806 switch (sw->state) { 807 case IDLE: 808 if (h_state) { 809 sw->state = JITTER; 810 /* In End of Conversion interrupt mode, the AD714X 811 * continuously generates hardware interrupts. 812 */ 813 touchpad_use_com_int(ad714x, idx); 814 dev_dbg(ad714x->dev, "touchpad %d touched\n", idx); 815 } 816 break; 817 818 case JITTER: 819 if (c_state == mask) { 820 touchpad_cal_sensor_val(ad714x, idx); 821 touchpad_cal_highest_stage(ad714x, idx); 822 if ((!touchpad_check_second_peak(ad714x, idx)) && 823 (!touchpad_check_endpoint(ad714x, idx))) { 824 dev_dbg(ad714x->dev, 825 "touchpad%d, 2 fingers or endpoint\n", 826 idx); 827 touchpad_cal_abs_pos(ad714x, idx); 828 sw->x_flt_pos = sw->x_abs_pos; 829 sw->y_flt_pos = sw->y_abs_pos; 830 sw->state = ACTIVE; 831 } 832 } 833 break; 834 835 case ACTIVE: 836 if (c_state == mask) { 837 if (h_state) { 838 touchpad_cal_sensor_val(ad714x, idx); 839 touchpad_cal_highest_stage(ad714x, idx); 840 if ((!touchpad_check_second_peak(ad714x, idx)) 841 && (!touchpad_check_endpoint(ad714x, idx))) { 842 touchpad_cal_abs_pos(ad714x, idx); 843 touchpad_cal_flt_pos(ad714x, idx); 844 input_report_abs(sw->input, ABS_X, 845 sw->x_flt_pos); 846 input_report_abs(sw->input, ABS_Y, 847 sw->y_flt_pos); 848 input_report_key(sw->input, BTN_TOUCH, 849 1); 850 } 851 } else { 852 /* When the user lifts off the sensor, configure 853 * the AD714X back to threshold interrupt mode. 854 */ 855 touchpad_use_thr_int(ad714x, idx); 856 sw->state = IDLE; 857 input_report_key(sw->input, BTN_TOUCH, 0); 858 dev_dbg(ad714x->dev, "touchpad %d released\n", 859 idx); 860 } 861 input_sync(sw->input); 862 } 863 break; 864 865 default: 866 break; 867 } 868 } 869 870 static int ad714x_hw_detect(struct ad714x_chip *ad714x) 871 { 872 unsigned short data; 873 874 ad714x->read(ad714x, AD714X_PARTID_REG, &data, 1); 875 switch (data & 0xFFF0) { 876 case AD7142_PARTID: 877 ad714x->product = 0x7142; 878 ad714x->version = data & 0xF; 879 dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n", 880 ad714x->version); 881 return 0; 882 883 case AD7143_PARTID: 884 ad714x->product = 0x7143; 885 ad714x->version = data & 0xF; 886 dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n", 887 ad714x->version); 888 return 0; 889 890 case AD7147_PARTID: 891 ad714x->product = 0x7147; 892 ad714x->version = data & 0xF; 893 dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n", 894 ad714x->version); 895 return 0; 896 897 case AD7148_PARTID: 898 ad714x->product = 0x7148; 899 ad714x->version = data & 0xF; 900 dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n", 901 ad714x->version); 902 return 0; 903 904 default: 905 dev_err(ad714x->dev, 906 "fail to detect AD714X captouch, read ID is %04x\n", 907 data); 908 return -ENODEV; 909 } 910 } 911 912 static void ad714x_hw_init(struct ad714x_chip *ad714x) 913 { 914 int i, j; 915 unsigned short reg_base; 916 unsigned short data; 917 918 /* configuration CDC and interrupts */ 919 920 for (i = 0; i < STAGE_NUM; i++) { 921 reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM; 922 for (j = 0; j < STAGE_CFGREG_NUM; j++) 923 ad714x->write(ad714x, reg_base + j, 924 ad714x->hw->stage_cfg_reg[i][j]); 925 } 926 927 for (i = 0; i < SYS_CFGREG_NUM; i++) 928 ad714x->write(ad714x, AD714X_SYSCFG_REG + i, 929 ad714x->hw->sys_cfg_reg[i]); 930 for (i = 0; i < SYS_CFGREG_NUM; i++) 931 ad714x->read(ad714x, AD714X_SYSCFG_REG + i, &data, 1); 932 933 ad714x->write(ad714x, AD714X_STG_CAL_EN_REG, 0xFFF); 934 935 /* clear all interrupts */ 936 ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); 937 } 938 939 static irqreturn_t ad714x_interrupt_thread(int irq, void *data) 940 { 941 struct ad714x_chip *ad714x = data; 942 int i; 943 944 mutex_lock(&ad714x->mutex); 945 946 ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); 947 948 for (i = 0; i < ad714x->hw->button_num; i++) 949 ad714x_button_state_machine(ad714x, i); 950 for (i = 0; i < ad714x->hw->slider_num; i++) 951 ad714x_slider_state_machine(ad714x, i); 952 for (i = 0; i < ad714x->hw->wheel_num; i++) 953 ad714x_wheel_state_machine(ad714x, i); 954 for (i = 0; i < ad714x->hw->touchpad_num; i++) 955 ad714x_touchpad_state_machine(ad714x, i); 956 957 mutex_unlock(&ad714x->mutex); 958 959 return IRQ_HANDLED; 960 } 961 962 struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, 963 ad714x_read_t read, ad714x_write_t write) 964 { 965 int i; 966 int error; 967 struct input_dev *input; 968 969 struct ad714x_platform_data *plat_data = dev_get_platdata(dev); 970 struct ad714x_chip *ad714x; 971 void *drv_mem; 972 unsigned long irqflags; 973 974 struct ad714x_button_drv *bt_drv; 975 struct ad714x_slider_drv *sd_drv; 976 struct ad714x_wheel_drv *wl_drv; 977 struct ad714x_touchpad_drv *tp_drv; 978 979 980 if (irq <= 0) { 981 dev_err(dev, "IRQ not configured!\n"); 982 error = -EINVAL; 983 return ERR_PTR(error); 984 } 985 986 if (dev_get_platdata(dev) == NULL) { 987 dev_err(dev, "platform data for ad714x doesn't exist\n"); 988 error = -EINVAL; 989 return ERR_PTR(error); 990 } 991 992 ad714x = devm_kzalloc(dev, sizeof(*ad714x) + sizeof(*ad714x->sw) + 993 sizeof(*sd_drv) * plat_data->slider_num + 994 sizeof(*wl_drv) * plat_data->wheel_num + 995 sizeof(*tp_drv) * plat_data->touchpad_num + 996 sizeof(*bt_drv) * plat_data->button_num, 997 GFP_KERNEL); 998 if (!ad714x) { 999 error = -ENOMEM; 1000 return ERR_PTR(error); 1001 } 1002 ad714x->hw = plat_data; 1003 1004 drv_mem = ad714x + 1; 1005 ad714x->sw = drv_mem; 1006 drv_mem += sizeof(*ad714x->sw); 1007 ad714x->sw->slider = sd_drv = drv_mem; 1008 drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num; 1009 ad714x->sw->wheel = wl_drv = drv_mem; 1010 drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num; 1011 ad714x->sw->touchpad = tp_drv = drv_mem; 1012 drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num; 1013 ad714x->sw->button = bt_drv = drv_mem; 1014 drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num; 1015 1016 ad714x->read = read; 1017 ad714x->write = write; 1018 ad714x->irq = irq; 1019 ad714x->dev = dev; 1020 1021 error = ad714x_hw_detect(ad714x); 1022 if (error) 1023 return ERR_PTR(error); 1024 1025 /* initialize and request sw/hw resources */ 1026 1027 ad714x_hw_init(ad714x); 1028 mutex_init(&ad714x->mutex); 1029 1030 /* a slider uses one input_dev instance */ 1031 if (ad714x->hw->slider_num > 0) { 1032 struct ad714x_slider_plat *sd_plat = ad714x->hw->slider; 1033 1034 for (i = 0; i < ad714x->hw->slider_num; i++) { 1035 input = devm_input_allocate_device(dev); 1036 if (!input) 1037 return ERR_PTR(-ENOMEM); 1038 1039 __set_bit(EV_ABS, input->evbit); 1040 __set_bit(EV_KEY, input->evbit); 1041 __set_bit(ABS_X, input->absbit); 1042 __set_bit(BTN_TOUCH, input->keybit); 1043 input_set_abs_params(input, 1044 ABS_X, 0, sd_plat->max_coord, 0, 0); 1045 1046 input->id.bustype = bus_type; 1047 input->id.product = ad714x->product; 1048 input->id.version = ad714x->version; 1049 input->name = "ad714x_captouch_slider"; 1050 input->dev.parent = dev; 1051 1052 error = input_register_device(input); 1053 if (error) 1054 return ERR_PTR(error); 1055 1056 sd_drv[i].input = input; 1057 } 1058 } 1059 1060 /* a wheel uses one input_dev instance */ 1061 if (ad714x->hw->wheel_num > 0) { 1062 struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel; 1063 1064 for (i = 0; i < ad714x->hw->wheel_num; i++) { 1065 input = devm_input_allocate_device(dev); 1066 if (!input) 1067 return ERR_PTR(-ENOMEM); 1068 1069 __set_bit(EV_KEY, input->evbit); 1070 __set_bit(EV_ABS, input->evbit); 1071 __set_bit(ABS_WHEEL, input->absbit); 1072 __set_bit(BTN_TOUCH, input->keybit); 1073 input_set_abs_params(input, 1074 ABS_WHEEL, 0, wl_plat->max_coord, 0, 0); 1075 1076 input->id.bustype = bus_type; 1077 input->id.product = ad714x->product; 1078 input->id.version = ad714x->version; 1079 input->name = "ad714x_captouch_wheel"; 1080 input->dev.parent = dev; 1081 1082 error = input_register_device(input); 1083 if (error) 1084 return ERR_PTR(error); 1085 1086 wl_drv[i].input = input; 1087 } 1088 } 1089 1090 /* a touchpad uses one input_dev instance */ 1091 if (ad714x->hw->touchpad_num > 0) { 1092 struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad; 1093 1094 for (i = 0; i < ad714x->hw->touchpad_num; i++) { 1095 input = devm_input_allocate_device(dev); 1096 if (!input) 1097 return ERR_PTR(-ENOMEM); 1098 1099 __set_bit(EV_ABS, input->evbit); 1100 __set_bit(EV_KEY, input->evbit); 1101 __set_bit(ABS_X, input->absbit); 1102 __set_bit(ABS_Y, input->absbit); 1103 __set_bit(BTN_TOUCH, input->keybit); 1104 input_set_abs_params(input, 1105 ABS_X, 0, tp_plat->x_max_coord, 0, 0); 1106 input_set_abs_params(input, 1107 ABS_Y, 0, tp_plat->y_max_coord, 0, 0); 1108 1109 input->id.bustype = bus_type; 1110 input->id.product = ad714x->product; 1111 input->id.version = ad714x->version; 1112 input->name = "ad714x_captouch_pad"; 1113 input->dev.parent = dev; 1114 1115 error = input_register_device(input); 1116 if (error) 1117 return ERR_PTR(error); 1118 1119 tp_drv[i].input = input; 1120 } 1121 } 1122 1123 /* all buttons use one input node */ 1124 if (ad714x->hw->button_num > 0) { 1125 struct ad714x_button_plat *bt_plat = ad714x->hw->button; 1126 1127 input = devm_input_allocate_device(dev); 1128 if (!input) { 1129 error = -ENOMEM; 1130 return ERR_PTR(error); 1131 } 1132 1133 __set_bit(EV_KEY, input->evbit); 1134 for (i = 0; i < ad714x->hw->button_num; i++) { 1135 bt_drv[i].input = input; 1136 __set_bit(bt_plat[i].keycode, input->keybit); 1137 } 1138 1139 input->id.bustype = bus_type; 1140 input->id.product = ad714x->product; 1141 input->id.version = ad714x->version; 1142 input->name = "ad714x_captouch_button"; 1143 input->dev.parent = dev; 1144 1145 error = input_register_device(input); 1146 if (error) 1147 return ERR_PTR(error); 1148 } 1149 1150 irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING; 1151 irqflags |= IRQF_ONESHOT; 1152 1153 error = devm_request_threaded_irq(dev, ad714x->irq, NULL, 1154 ad714x_interrupt_thread, 1155 irqflags, "ad714x_captouch", ad714x); 1156 if (error) { 1157 dev_err(dev, "can't allocate irq %d\n", ad714x->irq); 1158 return ERR_PTR(error); 1159 } 1160 1161 return ad714x; 1162 } 1163 EXPORT_SYMBOL(ad714x_probe); 1164 1165 #ifdef CONFIG_PM 1166 int ad714x_disable(struct ad714x_chip *ad714x) 1167 { 1168 unsigned short data; 1169 1170 dev_dbg(ad714x->dev, "%s enter\n", __func__); 1171 1172 mutex_lock(&ad714x->mutex); 1173 1174 data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3; 1175 ad714x->write(ad714x, AD714X_PWR_CTRL, data); 1176 1177 mutex_unlock(&ad714x->mutex); 1178 1179 return 0; 1180 } 1181 EXPORT_SYMBOL(ad714x_disable); 1182 1183 int ad714x_enable(struct ad714x_chip *ad714x) 1184 { 1185 dev_dbg(ad714x->dev, "%s enter\n", __func__); 1186 1187 mutex_lock(&ad714x->mutex); 1188 1189 /* resume to non-shutdown mode */ 1190 1191 ad714x->write(ad714x, AD714X_PWR_CTRL, 1192 ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]); 1193 1194 /* make sure the interrupt output line is not low level after resume, 1195 * otherwise we will get no chance to enter falling-edge irq again 1196 */ 1197 1198 ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3); 1199 1200 mutex_unlock(&ad714x->mutex); 1201 1202 return 0; 1203 } 1204 EXPORT_SYMBOL(ad714x_enable); 1205 #endif 1206 1207 MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver"); 1208 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 1209 MODULE_LICENSE("GPL"); 1210