lkml.org 
[lkml]   [2023]   [Jun]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v2 09/10] HID: i2c-hid: Do panel follower work on the system_wq
    Date
    Turning on an i2c-hid device can be a slow process. This is why
    i2c-hid devices use PROBE_PREFER_ASYNCHRONOUS. Unfortunately, when
    we're a panel follower the i2c-hid power up sequence now blocks the
    power on of the panel. Let's fix that by scheduling the work on the
    system_wq.

    Signed-off-by: Douglas Anderson <dianders@chromium.org>
    ---

    Changes in v2:
    - ihid_core_panel_prepare_work() is now static.
    - Improve documentation for smp_wmb().

    drivers/hid/i2c-hid/i2c-hid-core.c | 50 +++++++++++++++++++++++++++---
    1 file changed, 46 insertions(+), 4 deletions(-)

    diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
    index 368db3ae612f..de1a0624be08 100644
    --- a/drivers/hid/i2c-hid/i2c-hid-core.c
    +++ b/drivers/hid/i2c-hid/i2c-hid-core.c
    @@ -110,7 +110,9 @@ struct i2c_hid {

    struct i2chid_ops *ops;
    struct drm_panel_follower panel_follower;
    + struct work_struct panel_follower_prepare_work;
    bool is_panel_follower;
    + bool prepare_work_finished;
    };

    static const struct i2c_hid_quirks {
    @@ -1062,10 +1064,12 @@ static int i2c_hid_core_initial_power_up(struct i2c_hid *ihid)
    return ret;
    }

    -static int i2c_hid_core_panel_prepared(struct drm_panel_follower *follower)
    +static void ihid_core_panel_prepare_work(struct work_struct *work)
    {
    - struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower);
    + struct i2c_hid *ihid = container_of(work, struct i2c_hid,
    + panel_follower_prepare_work);
    struct hid_device *hid = ihid->hid;
    + int ret;

    /*
    * hid->version is set on the first power up. If it's still zero then
    @@ -1073,15 +1077,52 @@ static int i2c_hid_core_panel_prepared(struct drm_panel_follower *follower)
    * steps.
    */
    if (!hid->version)
    - return i2c_hid_core_initial_power_up(ihid);
    + ret = i2c_hid_core_initial_power_up(ihid);
    + else
    + ret = i2c_hid_core_resume(ihid);

    - return i2c_hid_core_resume(ihid);
    + if (ret)
    + dev_warn(&ihid->client->dev, "Power on failed: %d\n", ret);
    + else
    + WRITE_ONCE(ihid->prepare_work_finished, true);
    +
    + /*
    + * The work APIs provide a number of memory ordering guarantees
    + * including one that says that memory writes before schedule_work()
    + * are always visible to the work function, but they don't appear to
    + * guarantee that a write that happened in the work is visible after
    + * cancel_work_sync(). We'll add a write memory barrier here to match
    + * with i2c_hid_core_panel_unpreparing() to ensure that our write to
    + * prepare_work_finished is visible there.
    + */
    + smp_wmb();
    +}
    +
    +static int i2c_hid_core_panel_prepared(struct drm_panel_follower *follower)
    +{
    + struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower);
    +
    + /*
    + * Powering on a touchscreen can be a slow process. Queue the work to
    + * the system workqueue so we don't block the panel's power up.
    + */
    + WRITE_ONCE(ihid->prepare_work_finished, false);
    + schedule_work(&ihid->panel_follower_prepare_work);
    +
    + return 0;
    }

    static int i2c_hid_core_panel_unpreparing(struct drm_panel_follower *follower)
    {
    struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower);

    + cancel_work_sync(&ihid->panel_follower_prepare_work);
    +
    + /* Match with ihid_core_panel_prepare_work() */
    + smp_rmb();
    + if (!READ_ONCE(ihid->prepare_work_finished))
    + return 0;
    +
    return i2c_hid_core_suspend(ihid);
    }

    @@ -1124,6 +1165,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,

    init_waitqueue_head(&ihid->wait);
    mutex_init(&ihid->reset_lock);
    + INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work);

    /* we need to allocate the command buffer without knowing the maximum
    * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
    --
    2.41.0.162.gfafddb0af9-goog
    \
     
     \ /
      Last update: 2023-06-07 23:54    [W:9.257 / U:0.280 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site