]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: hda: Handle the jack polling always via a work
authorTakashi Iwai <tiwai@suse.de>
Mon, 23 Jun 2025 13:14:32 +0000 (15:14 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 23 Jun 2025 15:21:16 +0000 (17:21 +0200)
We used to call directly hda_jackpoll_work() from a couple of places
for updating the jack and notify to user-space, but this makes rather
the code flow fragile.  Namely, because of those direct calls,
hda_jackpoll_work() uses snd_hda_power_up_pm() and *_down_pm() calls
instead of the standard snd_hda_power_up() and *_down() calls.  The
latter pair assures the runtime PM resume sync, so it can avoid the
race against the PM callbacks gracefully, while the former pair may
continue if called concurrently, hence it may race (by design).

In this patch, we change the call pattern of hda_jackpoll_work(); now
all callers are replaced with the standard snd_hda_jack_report_sync()
and the additional schedule_delayed_work().

Since hda_jackpoll_work() is called only from the associated work,
it's always outside the PM code path, and we can safely use
snd_hda_power_up() and *_down() there instead.  This allows us to
remove the racy check of power-state in hda_jackpoll_work(), as well
as the tricky cancel_delayed_work() and rescheduling at
hda_codec_runtime_suspend().

Reported-by: Joakim Zhang <joakim.zhang@cixtech.com>
Closes: https://lore.kernel.org/20250619020844.2974160-1-joakim.zhang@cixtech.com
Tested-by: Joakim Zhang <joakim.zhang@cixtech.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20250623131437.10670-4-tiwai@suse.de
sound/pci/hda/hda_codec.c

index ea361390b82936a09e373cb96f7e98a89446d3a1..5508381a183321da12aff3a315e0ee5c149d9f23 100644 (file)
@@ -639,24 +639,16 @@ static void hda_jackpoll_work(struct work_struct *work)
        struct hda_codec *codec =
                container_of(work, struct hda_codec, jackpoll_work.work);
 
-       /* for non-polling trigger: we need nothing if already powered on */
-       if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core))
+       if (!codec->jackpoll_interval)
                return;
 
        /* the power-up/down sequence triggers the runtime resume */
-       snd_hda_power_up_pm(codec);
+       snd_hda_power_up(codec);
        /* update jacks manually if polling is required, too */
-       if (codec->jackpoll_interval) {
-               snd_hda_jack_set_dirty_all(codec);
-               snd_hda_jack_poll_all(codec);
-       }
-       snd_hda_power_down_pm(codec);
-
-       if (!codec->jackpoll_interval)
-               return;
-
-       schedule_delayed_work(&codec->jackpoll_work,
-                             codec->jackpoll_interval);
+       snd_hda_jack_set_dirty_all(codec);
+       snd_hda_jack_poll_all(codec);
+       schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval);
+       snd_hda_power_down(codec);
 }
 
 /* release all pincfg lists */
@@ -2895,12 +2887,12 @@ static void hda_call_codec_resume(struct hda_codec *codec)
                snd_hda_regmap_sync(codec);
        }
 
-       if (codec->jackpoll_interval)
-               hda_jackpoll_work(&codec->jackpoll_work.work);
-       else
-               snd_hda_jack_report_sync(codec);
+       snd_hda_jack_report_sync(codec);
        codec->core.dev.power.power_state = PMSG_ON;
        snd_hdac_leave_pm(&codec->core);
+       if (codec->jackpoll_interval)
+               schedule_delayed_work(&codec->jackpoll_work,
+                                     codec->jackpoll_interval);
 }
 
 static int hda_codec_runtime_suspend(struct device *dev)
@@ -2912,8 +2904,6 @@ static int hda_codec_runtime_suspend(struct device *dev)
        if (!codec->card)
                return 0;
 
-       cancel_delayed_work_sync(&codec->jackpoll_work);
-
        state = hda_call_codec_suspend(codec);
        if (codec->link_down_at_suspend ||
            (codec_has_clkstop(codec) && codec_has_epss(codec) &&
@@ -2921,10 +2911,6 @@ static int hda_codec_runtime_suspend(struct device *dev)
                snd_hdac_codec_link_down(&codec->core);
        snd_hda_codec_display_power(codec, false);
 
-       if (codec->bus->jackpoll_in_suspend &&
-               (dev->power.power_state.event != PM_EVENT_SUSPEND))
-               schedule_delayed_work(&codec->jackpoll_work,
-                                       codec->jackpoll_interval);
        return 0;
 }
 
@@ -3097,10 +3083,11 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       snd_hda_jack_report_sync(codec); /* call at the last init point */
        if (codec->jackpoll_interval)
-               hda_jackpoll_work(&codec->jackpoll_work.work);
-       else
-               snd_hda_jack_report_sync(codec); /* call at the last init point */
+               schedule_delayed_work(&codec->jackpoll_work,
+                                     codec->jackpoll_interval);
+
        sync_power_up_states(codec);
        return 0;
 }