Re: [PATCH v5 3/3] input: misc: Add Qualcomm SPMI PMIC haptics driver
[email protected] Wed, 05 Aug 2026 06:28:57 +0000
| Newsgroups | org.kernel.vger.linux-input,org.kernel.vger.linux-devicetree |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] Multiplexing play_work for both playback requests and duration timers causes race conditions where active effect duration timers are overwritten, leading to indefinite playback. - [High] The driver permanently disables the playback workqueue in `qcom_haptics_close()` but lacks an `open` callback to re-enable it, rendering the device inoperable after the first close. - [High] Manually disabling the hardware module in `close()` desynchronizes the runtime PM state, causing subsequent playbacks to silently fail if started quickly. -- --- Patch [3]: [PATCH v5 3/3] input: misc: Add Qualcomm SPMI PMIC haptics driver --- commit 64b23ca2ca86ee295bdc5ad669c51fb6d603b0fc Author: Fenglin Wu <[email protected]> input: misc: Add Qualcomm SPMI PMIC haptics driver Add an initial driver for the Qualcomm PMIH0108 PMIC haptics module, named as HAP530_HV. This module supports several play modes, including DIRECT_PLAY, FIFO, PAT_MEM, and SWR, each with distinct data sourcing and hardware data handling logic. Currently, the driver provides support for two play modes using the input force-feedback framework: FF_CONSTANT effect for DIRECT_PLAY mode and FF_PERIODIC effect with FF_CUSTOM waveform for FIFO mode. diff --git a/drivers/input/misc/qcom-spmi-haptics.c b/drivers/input/misc/qcom-spmi-haptics.c --- /dev/null +++ b/drivers/input/misc/qcom-spmi-haptics.c [ ... ] > +static void haptics_play_work(struct work_struct *work) > +{ > + struct qcom_haptics *h = container_of(to_delayed_work(work), > + struct qcom_haptics, play_work); > + struct haptics_play_req *req = haptics_dequeue_play_req(h); > + bool rearmed = false; > + u32 length_us; > + int ret; > + > + guard(mutex)(&h->play_lock); > + > + if (!req) { > + /* > + * Not a new request: the play-duration re-arm timer fired > + * so stop the play > + */ > + if (h->active_effect_id != -1) > + haptics_stop(h, false); > + } else if (req->play) { > + ret = haptics_start(h, req->effect_id); > + if (ret) { > + dev_err(h->dev, "play haptics failed, ret=%d\n", ret); > + } else { > + /* > + * Re-arm the work to stop the play or advance > + * to next play after the play duration > + */ > + length_us = h->effects[req->effect_id].length_us; > + if (length_us) { > + schedule_delayed_work(&h->play_work, > + usecs_to_jiffies(length_us)); > + rearmed = true; > + } > + } > + } else { > + if (req->effect_id == h->active_effect_id) > + haptics_stop(h, false); > + } > + > + kfree(req); > + > + /* Wait for the re-arm to stop the play */ > + if (rearmed) > + return; > + > + /* Handle the remaining requests if the queue is not empty */ > + guard(spinlock_irqsave)(&h->play_queue_lock); > + if (!list_empty(&h->play_queue)) > + schedule_delayed_work(&h->play_work, 0); > +} [Severity: High] Does this code handle the active effect's stop timer correctly when a STOP request for a different effect arrives? If an effect is currently playing with a scheduled stop timer, and userspace sends a STOP request for a different inactive effect, the worker sees that the STOP request is for an inactive effect and drops it. However, it appears it fails to re-arm the duration timer for the active effect, which could result in the active effect vibrating indefinitely. [ ... ] > +static int haptics_playback(struct input_dev *dev, int effect_id, int val) > +{ > + struct qcom_haptics *h = input_get_drvdata(dev); > + int ret; > + > + ret = haptics_enqueue_play_req(h, effect_id, val > 0); > + if (ret) > + return ret; > + > + if (val > 0) > + /* > + * Queue the play. If a duration re-arm is already pending this > + * is a no-op, so the new play waits for the current effect to > + * finish before the worker dequeues it. > + */ > + schedule_delayed_work(&h->play_work, 0); > + else > + /* > + * Run the worker now, cancelling any pending duration re-arm, > + * so an explicit stop takes effect immediately. > + */ > + mod_delayed_work(system_percpu_wq, &h->play_work, 0); > + > + return 0; > +} [Severity: High] Can calling mod_delayed_work() here overwrite an active duration timer? If userspace sends a STOP request for a different inactive effect, mod_delayed_work() is called unconditionally, cancelling the active effect's duration timer before the worker gets a chance to process the inactive STOP request. [ ... ] > +static void qcom_haptics_close(struct input_dev *input) > +{ > + struct qcom_haptics *h = input_get_drvdata(input); > + > + cancel_work_sync(&h->gain_work); > + haptics_enable_play_work(h, false); > + scoped_guard(mutex, &h->play_lock) { > + haptics_queue_flush(h); > + haptics_stop(h, false); > + } > + > + haptics_enable_module(h, false); > +} [Severity: High] Does disabling play_work here permanently render the device inoperable? When the input device is closed, haptics_enable_play_work() uses disable_delayed_work_sync() which sets the WORK_STRUCT_DISABLE bit on the play_work item. Because there is no corresponding open() callback registered to re-enable it, subsequent playback requests will fail to schedule the work after the device is opened again. [Severity: High] Can manually disabling the hardware module here desynchronize the runtime PM state? Calling haptics_enable_module() directly bypasses the PM runtime framework. Earlier in the close sequence, haptics_stop() calls pm_runtime_put_autosuspend() which leaves the PM state as RPM_ACTIVE for the 1000ms autosuspend delay. If userspace reopens the device and requests playback within this window, haptics_start() will see the device as already active and skip calling qcom_haptics_runtime_resume(). The hardware module will remain disabled, causing subsequent register writes to be silently ignored. -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=3