Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: cyapa - fully support runtime suspend power management

Fix the the runtime suspend power management not working issue when system
starts up and before user touches the trackpad device.
TEST=test on Chromebook.

Signed-off-by: Dudley Du <dudl@cypress.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Dudley Du and committed by
Dmitry Torokhov
757cae5a 945525ee

+70 -34
+51 -22
drivers/input/mouse/cyapa.c
··· 367 367 { 368 368 struct cyapa *cyapa = input_get_drvdata(input); 369 369 struct i2c_client *client = cyapa->client; 370 + struct device *dev = &client->dev; 370 371 int error; 371 372 372 373 error = mutex_lock_interruptible(&cyapa->state_sync_lock); ··· 381 380 * when in operational mode. 382 381 */ 383 382 error = cyapa->ops->set_power_mode(cyapa, 384 - PWR_MODE_FULL_ACTIVE, 0); 383 + PWR_MODE_FULL_ACTIVE, 0, false); 385 384 if (error) { 386 - dev_warn(&client->dev, 387 - "set active power failed: %d\n", error); 385 + dev_warn(dev, "set active power failed: %d\n", error); 388 386 goto out; 389 387 } 390 388 } else { ··· 395 395 } 396 396 397 397 enable_irq(client->irq); 398 - if (!pm_runtime_enabled(&client->dev)) { 399 - pm_runtime_set_active(&client->dev); 400 - pm_runtime_enable(&client->dev); 398 + if (!pm_runtime_enabled(dev)) { 399 + pm_runtime_set_active(dev); 400 + pm_runtime_enable(dev); 401 401 } 402 + 403 + pm_runtime_get_sync(dev); 404 + pm_runtime_mark_last_busy(dev); 405 + pm_runtime_put_sync_autosuspend(dev); 402 406 out: 403 407 mutex_unlock(&cyapa->state_sync_lock); 404 408 return error; ··· 412 408 { 413 409 struct cyapa *cyapa = input_get_drvdata(input); 414 410 struct i2c_client *client = cyapa->client; 411 + struct device *dev = &cyapa->client->dev; 415 412 416 413 mutex_lock(&cyapa->state_sync_lock); 417 414 418 415 disable_irq(client->irq); 419 - if (pm_runtime_enabled(&client->dev)) 420 - pm_runtime_disable(&client->dev); 421 - pm_runtime_set_suspended(&client->dev); 416 + if (pm_runtime_enabled(dev)) 417 + pm_runtime_disable(dev); 418 + pm_runtime_set_suspended(dev); 422 419 423 420 if (cyapa->operational) 424 - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 421 + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); 425 422 426 423 mutex_unlock(&cyapa->state_sync_lock); 427 424 } ··· 532 527 */ 533 528 if (!input || cyapa->operational) 534 529 cyapa->ops->set_power_mode(cyapa, 535 - PWR_MODE_FULL_ACTIVE, 0); 530 + PWR_MODE_FULL_ACTIVE, 0, false); 536 531 /* Gen3 always using polling mode for command. */ 537 532 if (cyapa->gen >= CYAPA_GEN5) 538 533 enable_irq(cyapa->client->irq); ··· 547 542 if (cyapa->gen >= CYAPA_GEN5) 548 543 disable_irq(cyapa->client->irq); 549 544 if (!input || cyapa->operational) 550 - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 545 + cyapa->ops->set_power_mode(cyapa, 546 + PWR_MODE_OFF, 0, false); 551 547 } 552 548 } 553 549 ··· 615 609 616 610 /* Power down the device until we need it. */ 617 611 if (cyapa->operational) 618 - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 612 + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); 619 613 620 614 return 0; 621 615 } ··· 631 625 632 626 /* Avoid command failures when TP was in OFF state. */ 633 627 if (cyapa->operational) 634 - cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); 628 + cyapa->ops->set_power_mode(cyapa, 629 + PWR_MODE_FULL_ACTIVE, 0, false); 635 630 636 631 error = cyapa_detect(cyapa); 637 632 if (error) ··· 651 644 if (!input || !input->users) { 652 645 /* Reset to power OFF state to save power when no user open. */ 653 646 if (cyapa->operational) 654 - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); 647 + cyapa->ops->set_power_mode(cyapa, 648 + PWR_MODE_OFF, 0, false); 655 649 } else if (!error && cyapa->operational) { 656 650 /* 657 651 * Make sure only enable runtime PM when device is ··· 660 652 */ 661 653 pm_runtime_set_active(dev); 662 654 pm_runtime_enable(dev); 655 + 656 + pm_runtime_get_sync(dev); 657 + pm_runtime_mark_last_busy(dev); 658 + pm_runtime_put_sync_autosuspend(dev); 663 659 } 664 660 665 661 return error; ··· 673 661 { 674 662 struct cyapa *cyapa = dev_id; 675 663 struct device *dev = &cyapa->client->dev; 664 + int error; 676 665 677 - pm_runtime_get_sync(dev); 678 666 if (device_may_wakeup(dev)) 679 667 pm_wakeup_event(dev, 0); 680 668 ··· 693 681 goto out; 694 682 } 695 683 696 - if (!cyapa->operational || cyapa->ops->irq_handler(cyapa)) { 684 + if (cyapa->operational) { 685 + error = cyapa->ops->irq_handler(cyapa); 686 + 687 + /* 688 + * Apply runtime power management to touch report event 689 + * except the events caused by the command responses. 690 + * Note: 691 + * It will introduce about 20~40 ms additional delay 692 + * time in receiving for first valid touch report data. 693 + * The time is used to execute device runtime resume 694 + * process. 695 + */ 696 + pm_runtime_get_sync(dev); 697 + pm_runtime_mark_last_busy(dev); 698 + pm_runtime_put_sync_autosuspend(dev); 699 + } 700 + 701 + if (!cyapa->operational || error) { 697 702 if (!mutex_trylock(&cyapa->state_sync_lock)) { 698 703 cyapa->ops->sort_empty_output_data(cyapa, 699 704 NULL, NULL, NULL); ··· 722 693 } 723 694 724 695 out: 725 - pm_runtime_mark_last_busy(dev); 726 - pm_runtime_put_sync_autosuspend(dev); 727 696 return IRQ_HANDLED; 728 697 } 729 698 ··· 1362 1335 power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode 1363 1336 : PWR_MODE_OFF; 1364 1337 error = cyapa->ops->set_power_mode(cyapa, power_mode, 1365 - cyapa->suspend_sleep_time); 1338 + cyapa->suspend_sleep_time, true); 1366 1339 if (error) 1367 1340 dev_err(dev, "suspend set power mode failed: %d\n", 1368 1341 error); ··· 1416 1389 1417 1390 error = cyapa->ops->set_power_mode(cyapa, 1418 1391 cyapa->runtime_suspend_power_mode, 1419 - cyapa->runtime_suspend_sleep_time); 1392 + cyapa->runtime_suspend_sleep_time, 1393 + false); 1420 1394 if (error) 1421 1395 dev_warn(dev, "runtime suspend failed: %d\n", error); 1422 1396 ··· 1429 1401 struct cyapa *cyapa = dev_get_drvdata(dev); 1430 1402 int error; 1431 1403 1432 - error = cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); 1404 + error = cyapa->ops->set_power_mode(cyapa, 1405 + PWR_MODE_FULL_ACTIVE, 0, false); 1433 1406 if (error) 1434 1407 dev_warn(dev, "runtime resume failed: %d\n", error); 1435 1408
+1 -1
drivers/input/mouse/cyapa.h
··· 273 273 int (*sort_empty_output_data)(struct cyapa *, 274 274 u8 *, int *, cb_sort); 275 275 276 - int (*set_power_mode)(struct cyapa *, u8, u16); 276 + int (*set_power_mode)(struct cyapa *, u8, u16, bool); 277 277 278 278 int (*set_proximity)(struct cyapa *, bool); 279 279 };
+2 -2
drivers/input/mouse/cyapa_gen3.c
··· 950 950 * Device power mode can only be set when device is in operational mode. 951 951 */ 952 952 static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, 953 - u16 always_unused) 953 + u16 always_unused, bool is_suspend_unused) 954 954 { 955 955 int ret; 956 956 u8 power; ··· 1112 1112 * may cause problems, so we set the power mode first here. 1113 1113 */ 1114 1114 error = cyapa_gen3_set_power_mode(cyapa, 1115 - PWR_MODE_FULL_ACTIVE, 0); 1115 + PWR_MODE_FULL_ACTIVE, 0, false); 1116 1116 if (error) 1117 1117 dev_err(dev, "%s: set full power mode failed: %d\n", 1118 1118 __func__, error);
+14 -7
drivers/input/mouse/cyapa_gen5.c
··· 19 19 #include <linux/slab.h> 20 20 #include <asm/unaligned.h> 21 21 #include <linux/crc-itu-t.h> 22 + #include <linux/pm_runtime.h> 22 23 #include "cyapa.h" 23 24 24 25 ··· 1566 1565 } 1567 1566 1568 1567 static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, 1569 - u8 power_mode, u16 sleep_time) 1568 + u8 power_mode, u16 sleep_time, bool is_suspend) 1570 1569 { 1571 1570 struct device *dev = &cyapa->client->dev; 1572 1571 u8 power_state; ··· 1574 1573 1575 1574 if (cyapa->state != CYAPA_STATE_GEN5_APP) 1576 1575 return 0; 1577 - 1578 - /* Dump all the report data before do power mode commmands. */ 1579 - cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); 1580 1576 1581 1577 if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { 1582 1578 /* ··· 1677 1679 * is suspending which may cause interrupt line unable to be 1678 1680 * asserted again. 1679 1681 */ 1680 - cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); 1681 - cyapa_gen5_disable_pip_report(cyapa); 1682 + if (is_suspend) 1683 + cyapa_gen5_disable_pip_report(cyapa); 1682 1684 1683 1685 PIP_DEV_SET_PWR_STATE(cyapa, 1684 1686 cyapa_sleep_time_to_pwr_cmd(sleep_time)); ··· 2513 2515 * the device state is required. 2514 2516 */ 2515 2517 error = cyapa_gen5_set_power_mode(cyapa, 2516 - PWR_MODE_FULL_ACTIVE, 0); 2518 + PWR_MODE_FULL_ACTIVE, 0, false); 2517 2519 if (error) 2518 2520 dev_warn(dev, "%s: failed to set power active mode.\n", 2519 2521 __func__); ··· 2755 2757 /* 2756 2758 * Device wake event from deep sleep mode for touch. 2757 2759 * This interrupt event is used to wake system up. 2760 + * 2761 + * Note: 2762 + * It will introduce about 20~40 ms additional delay 2763 + * time in receiving for first valid touch report data. 2764 + * The time is used to execute device runtime resume 2765 + * process. 2758 2766 */ 2767 + pm_runtime_get_sync(dev); 2768 + pm_runtime_mark_last_busy(dev); 2769 + pm_runtime_put_sync_autosuspend(dev); 2759 2770 return 0; 2760 2771 } else if (report_id != PIP_TOUCH_REPORT_ID && 2761 2772 report_id != PIP_BTN_REPORT_ID &&
+2 -2
drivers/input/mouse/cyapa_gen6.c
··· 429 429 } 430 430 431 431 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, 432 - u8 power_mode, u16 sleep_time) 432 + u8 power_mode, u16 sleep_time, bool is_suspend) 433 433 { 434 434 struct device *dev = &cyapa->client->dev; 435 435 struct gen6_interval_setting *interval_setting = ··· 693 693 * the device state is required. 694 694 */ 695 695 error = cyapa_gen6_set_power_mode(cyapa, 696 - PWR_MODE_FULL_ACTIVE, 0); 696 + PWR_MODE_FULL_ACTIVE, 0, false); 697 697 if (error) 698 698 dev_warn(dev, "%s: failed to set power active mode.\n", 699 699 __func__);