]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: hda: Introduce hda_codec_driver ops
authorTakashi Iwai <tiwai@suse.de>
Wed, 9 Jul 2025 16:04:12 +0000 (18:04 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 11 Jul 2025 07:55:37 +0000 (09:55 +0200)
Until now, we use "patch_ops" embedded in hda_codec object for
defining the callbacks that are used in various places to manage
HD-audio codec.  But from the device driver POV, this should have been
rather the driver ops, instead of the callbacks in the codec object.

This patch defines the driver ops for HD-audio codec driver as the
replacement.  We reuse the same struct hda_codec_ops, and this is put
as hda_codec_driver.ops.  When the driver->ops callbacks are defined,
they are called primarily instead of codec->patch_ops callbacks.

With converting to the driver ops, there is no need to pass the ugly
patch_ops handling in hda_device_id tables.  That is, driver_data
field of hda_device_id becomes really optional and it can be used for
passing the codec-specific data (e.g. specifying a model).

The codec entries after the conversion should be with HDA_CODEC_ID()
and co, instead of the former HDA_CODEC_ENTRY().

Once after converting all codec drivers to use driver ops, we can get
rid of codec patch_ops.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20250709160434.1859-10-tiwai@suse.de
include/sound/hda_codec.h
sound/hda/common/bind.c
sound/hda/common/codec.c
sound/hda/common/hda_local.h
sound/soc/codecs/hda.c
sound/soc/codecs/hdac_hda.c

index c1fe6290d04dcb332569244d8ceac0d58f33e4d7..a725ac48c20c4ba4196e08288b891deb54bce209 100644 (file)
@@ -27,6 +27,7 @@ struct hda_beep;
 struct hda_codec;
 struct hda_pcm;
 struct hda_pcm_stream;
+struct hda_codec_ops;
 
 /*
  * codec bus
@@ -79,6 +80,17 @@ typedef int (*hda_codec_patch_t)(struct hda_codec *);
 #define HDA_CODEC_ID_GENERIC_HDMI      0x00000101
 #define HDA_CODEC_ID_GENERIC           0x00000201
 
+#define HDA_CODEC_ID_REV_MODEL(_vid, _rev, _name, _model)        \
+       { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+         .api_version = HDA_DEV_LEGACY, .driver_data = (_model) }
+#define HDA_CODEC_ID_MODEL(_vid, _name, _model)          \
+       HDA_CODEC_ID_REV_MODEL(_vid, 0, _name, _model)
+#define HDA_CODEC_ID_REV(_vid, _rev, _name) \
+       HDA_CODEC_ID_REV_MODEL(_vid, _rev, _name, 0)
+#define HDA_CODEC_ID(_vid, _name) \
+       HDA_CODEC_ID_REV(_vid, 0, _name)
+
+/* old macros for patch_ops -- to be deprecated */
 #define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
        { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
          .api_version = HDA_DEV_LEGACY, \
@@ -89,8 +101,12 @@ typedef int (*hda_codec_patch_t)(struct hda_codec *);
 struct hda_codec_driver {
        struct hdac_driver core;
        const struct hda_device_id *id;
+       const struct hda_codec_ops *ops;
 };
 
+#define hda_codec_to_driver(codec) \
+       container_of((codec)->core.dev.driver, struct hda_codec_driver, core.driver)
+
 int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
                               struct module *owner);
 #define hda_codec_driver_register(drv) \
@@ -102,6 +118,8 @@ void hda_codec_driver_unregister(struct hda_codec_driver *drv);
 
 /* ops set by the preset patch */
 struct hda_codec_ops {
+       int (*probe)(struct hda_codec *codec, const struct hda_device_id *id);
+       void (*remove)(struct hda_codec *codec);
        int (*build_controls)(struct hda_codec *codec);
        int (*build_pcms)(struct hda_codec *codec);
        int (*init)(struct hda_codec *codec);
@@ -184,7 +202,7 @@ struct hda_codec {
        /* set by patch */
        struct hda_codec_ops patch_ops;
 
-       /* PCM to create, set by patch_ops.build_pcms callback */
+       /* PCM to create, set by hda_codec_ops.build_pcms callback */
        struct list_head pcm_list_head;
        refcount_t pcm_ref;
        wait_queue_head_t remove_sleep;
@@ -478,7 +496,11 @@ extern const struct dev_pm_ops hda_codec_driver_pm;
 static inline
 int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
-       if (codec->patch_ops.check_power_status)
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+       if (driver->ops && driver->ops->check_power_status)
+               return driver->ops->check_power_status(codec, nid);
+       else if (codec->patch_ops.check_power_status)
                return codec->patch_ops.check_power_status(codec, nid);
        return 0;
 }
index df8f88beddd0738b108be129b2a136141b3eae6b..56975178f53390010c80f920fbadd951749c9bb0 100644 (file)
@@ -42,6 +42,7 @@ static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *dr
 static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
 {
        struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
 
        /* ignore unsol events during shutdown */
        if (codec->card->shutdown || codec->bus->shutdown)
@@ -51,7 +52,9 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
        if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
                return;
 
-       if (codec->patch_ops.unsol_event)
+       if (driver->ops && driver->ops->unsol_event)
+               driver->ops->unsol_event(codec, ev);
+       else if (codec->patch_ops.unsol_event)
                codec->patch_ops.unsol_event(codec, ev);
 }
 
@@ -87,6 +90,7 @@ static int hda_codec_driver_probe(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
        struct module *owner = dev->driver->owner;
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        hda_codec_patch_t patch;
        int err;
 
@@ -111,11 +115,17 @@ static int hda_codec_driver_probe(struct device *dev)
                goto error;
        }
 
-       patch = (hda_codec_patch_t)codec->preset->driver_data;
-       if (patch) {
-               err = patch(codec);
+       if (driver->ops && driver->ops->probe) {
+               err = driver->ops->probe(codec, codec->preset);
                if (err < 0)
                        goto error_module_put;
+       } else {
+               patch = (hda_codec_patch_t)codec->preset->driver_data;
+               if (patch) {
+                       err = patch(codec);
+                       if (err < 0)
+                               goto error_module_put;
+               }
        }
 
        err = snd_hda_codec_build_pcms(codec);
@@ -136,7 +146,9 @@ static int hda_codec_driver_probe(struct device *dev)
        return 0;
 
  error_module:
-       if (codec->patch_ops.free)
+       if (driver->ops && driver->ops->remove)
+               driver->ops->remove(codec);
+       else if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
  error_module_put:
        module_put(owner);
@@ -150,6 +162,7 @@ static int hda_codec_driver_probe(struct device *dev)
 static int hda_codec_driver_remove(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
 
        if (codec->bus->core.ext_ops) {
                if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach))
@@ -163,7 +176,9 @@ static int hda_codec_driver_remove(struct device *dev)
                wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
        snd_power_sync_ref(codec->bus->card);
 
-       if (codec->patch_ops.free)
+       if (driver->ops && driver->ops->remove)
+               driver->ops->remove(codec);
+       else if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
        snd_hda_codec_cleanup_for_unbind(codec);
        codec->preset = NULL;
index cb72e9655c8a62db624cd53b51f8128eb830391e..8899be764d689aea16eca77bcfc5f629143941ae 100644 (file)
@@ -1114,6 +1114,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                                u32 stream_tag,
                                int channel_id, int format)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        struct hda_codec *c;
        struct hda_cvt_setup *p;
        int type;
@@ -1129,7 +1130,9 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        if (!p)
                return;
 
-       if (codec->patch_ops.stream_pm)
+       if (driver->ops && driver->ops->stream_pm)
+               driver->ops->stream_pm(codec, nid, true);
+       else if (codec->patch_ops.stream_pm)
                codec->patch_ops.stream_pm(codec, nid, true);
        if (codec->pcm_format_first)
                update_pcm_format(codec, p, nid, format);
@@ -1190,7 +1193,9 @@ EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
 static void really_cleanup_stream(struct hda_codec *codec,
                                  struct hda_cvt_setup *q)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        hda_nid_t nid = q->nid;
+
        if (q->stream_tag || q->channel_id)
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
        if (q->format_id)
@@ -1198,7 +1203,9 @@ static void really_cleanup_stream(struct hda_codec *codec,
 );
        memset(q, 0, sizeof(*q));
        q->nid = nid;
-       if (codec->patch_ops.stream_pm)
+       if (driver->ops && driver->ops->stream_pm)
+               driver->ops->stream_pm(codec, nid, false);
+       else if (codec->patch_ops.stream_pm)
                codec->patch_ops.stream_pm(codec, nid, false);
 }
 
@@ -2746,6 +2753,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
 static unsigned int hda_set_power_state(struct hda_codec *codec,
                                        unsigned int power_state)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
        int count;
        unsigned int state;
@@ -2762,7 +2770,10 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
 
        /* repeat power states setting at most 10 times*/
        for (count = 0; count < 10; count++) {
-               if (codec->patch_ops.set_power_state)
+               /* might be called before binding to driver, too */
+               if (driver && driver->ops && driver->ops->set_power_state)
+                       driver->ops->set_power_state(codec, fg, power_state);
+               else if (codec->patch_ops.set_power_state)
                        codec->patch_ops.set_power_state(codec, fg,
                                                         power_state);
                else {
@@ -2842,10 +2853,13 @@ void snd_hda_update_power_acct(struct hda_codec *codec)
  */
 static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        unsigned int state;
 
        snd_hdac_enter_pm(&codec->core);
-       if (codec->patch_ops.suspend)
+       if (driver->ops && driver->ops->suspend)
+               driver->ops->suspend(codec);
+       else if (codec->patch_ops.suspend)
                codec->patch_ops.suspend(codec);
        if (!codec->no_stream_clean_at_suspend)
                hda_cleanup_all_streams(codec);
@@ -2860,6 +2874,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
  */
 static void hda_call_codec_resume(struct hda_codec *codec)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
        snd_hdac_enter_pm(&codec->core);
        if (codec->core.regmap)
                regcache_mark_dirty(codec->core.regmap);
@@ -2870,11 +2886,12 @@ static void hda_call_codec_resume(struct hda_codec *codec)
        restore_shutup_pins(codec);
        hda_exec_init_verbs(codec);
        snd_hda_jack_set_dirty_all(codec);
-       if (codec->patch_ops.resume)
+       if (driver->ops && driver->ops->resume)
+               driver->ops->resume(codec);
+       else if (codec->patch_ops.resume)
                codec->patch_ops.resume(codec);
        else {
-               if (codec->patch_ops.init)
-                       codec->patch_ops.init(codec);
+               snd_hda_codec_init(codec);
                snd_hda_regmap_sync(codec);
        }
 
@@ -3059,15 +3076,20 @@ EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
 
 int snd_hda_codec_build_controls(struct hda_codec *codec)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        int err = 0;
+
        hda_exec_init_verbs(codec);
        /* continue to initialize... */
-       if (codec->patch_ops.init)
-               err = codec->patch_ops.init(codec);
-       if (!err && codec->patch_ops.build_controls)
-               err = codec->patch_ops.build_controls(codec);
-       if (err < 0)
-               return err;
+       err = snd_hda_codec_init(codec);
+       if (!err) {
+               if (driver->ops && driver->ops->build_controls)
+                       err = driver->ops->build_controls(codec);
+               else if (codec->patch_ops.build_controls)
+                       err = codec->patch_ops.build_controls(codec);
+               if (err < 0)
+                       return err;
+       }
 
        /* we create chmaps here instead of build_pcms */
        err = add_std_chmaps(codec);
@@ -3253,16 +3275,20 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
 /* call build_pcms ops of the given codec and set up the default parameters */
 int snd_hda_codec_parse_pcms(struct hda_codec *codec)
 {
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        struct hda_pcm *cpcm;
        int err;
 
        if (!list_empty(&codec->pcm_list_head))
                return 0; /* already parsed */
 
-       if (!codec->patch_ops.build_pcms)
+       if (driver->ops && driver->ops->build_pcms)
+               err = driver->ops->build_pcms(codec);
+       else if (codec->patch_ops.build_pcms)
+               err = codec->patch_ops.build_pcms(codec);
+       else
                return 0;
 
-       err = codec->patch_ops.build_pcms(codec);
        if (err < 0) {
                codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
                          codec->core.addr, err);
index 428aa5a06ead4b642bc8ef8cceb802c8b29476ff..654fe1156d5688ebafd90eb37a3c6a9b8f2d399d 100644 (file)
@@ -652,6 +652,17 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
 
 void snd_hda_codec_shutdown(struct hda_codec *codec);
 
+static inline int snd_hda_codec_init(struct hda_codec *codec)
+{
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+       if (driver->ops && driver->ops->init)
+               return driver->ops->init(codec);
+       else if (codec->patch_ops.init)
+               return codec->patch_ops.init(codec);
+       return 0;
+}
+
 /*
  * AMP control callbacks
  */
index dc7794c9ac44cedc43b4d37e2661ab6c10d8cade..ddb31001657ea24b219d7eced28fbb90761b656a 100644 (file)
@@ -173,6 +173,7 @@ EXPORT_SYMBOL_GPL(hda_codec_probe_complete);
 static int hda_codec_probe(struct snd_soc_component *component)
 {
        struct hda_codec *codec = dev_to_hda_codec(component->dev);
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        struct hdac_device *hdev = &codec->core;
        struct hdac_bus *bus = hdev->bus;
        struct hdac_ext_link *hlink;
@@ -214,14 +215,19 @@ static int hda_codec_probe(struct snd_soc_component *component)
                goto err;
        }
 
-       patch = (hda_codec_patch_t)codec->preset->driver_data;
-       if (!patch) {
-               dev_err(&hdev->dev, "no patch specified\n");
-               ret = -EINVAL;
-               goto err;
+       if (driver->ops && driver->ops->probe) {
+               ret = driver->ops->probe(codec, codec->preset);
+       } else {
+               patch = (hda_codec_patch_t)codec->preset->driver_data;
+               if (!patch) {
+                       dev_err(&hdev->dev, "no patch specified\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ret = patch(codec);
        }
 
-       ret = patch(codec);
        if (ret < 0) {
                dev_err(&hdev->dev, "codec init failed: %d\n", ret);
                goto err;
@@ -252,7 +258,9 @@ static int hda_codec_probe(struct snd_soc_component *component)
 complete_err:
        hda_codec_unregister_dais(codec, component);
 parse_pcms_err:
-       if (codec->patch_ops.free)
+       if (driver->ops && driver->ops->remove)
+               driver->ops->remove(codec);
+       else if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
 err:
        snd_hda_codec_cleanup_for_unbind(codec);
@@ -271,6 +279,7 @@ device_new_err:
 static void hda_codec_remove(struct snd_soc_component *component)
 {
        struct hda_codec *codec = dev_to_hda_codec(component->dev);
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        struct hdac_device *hdev = &codec->core;
        struct hdac_bus *bus = hdev->bus;
        struct hdac_ext_link *hlink;
@@ -281,7 +290,9 @@ static void hda_codec_remove(struct snd_soc_component *component)
 
        hda_codec_unregister_dais(codec, component);
 
-       if (codec->patch_ops.free)
+       if (driver->ops && driver->ops->remove)
+               driver->ops->remove(codec);
+       else if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
 
        snd_hda_codec_cleanup_for_unbind(codec);
index 29c88de5508b825de60a11f92bad57915f6fb76c..7bb7845d5e437ad438d256fd3eb170f0c96647ab 100644 (file)
@@ -409,6 +409,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
                        snd_soc_component_get_dapm(component);
        struct hdac_device *hdev = &hda_pvt->codec->core;
        struct hda_codec *hcodec = hda_pvt->codec;
+       struct hda_codec_driver *driver = hda_codec_to_driver(hcodec);
        struct hdac_ext_link *hlink;
        hda_codec_patch_t patch;
        int ret;
@@ -484,15 +485,23 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
                goto error_pm;
        }
 
-       patch = (hda_codec_patch_t)hcodec->preset->driver_data;
-       if (patch) {
-               ret = patch(hcodec);
+       if (driver->ops && driver->ops->probe) {
+               ret = driver->ops->probe(hcodec, hcodec->preset);
                if (ret < 0) {
-                       dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret);
+                       dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret);
                        goto error_regmap;
                }
        } else {
-               dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__);
+               patch = (hda_codec_patch_t)hcodec->preset->driver_data;
+               if (patch) {
+                       ret = patch(hcodec);
+                       if (ret < 0) {
+                               dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret);
+                               goto error_regmap;
+                       }
+               } else {
+                       dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__);
+               }
        }
 
        ret = snd_hda_codec_parse_pcms(hcodec);
@@ -531,7 +540,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
        return 0;
 
 error_patch:
-       if (hcodec->patch_ops.free)
+       if (driver->ops && driver->ops->remove)
+               driver->ops->remove(hcodec);
+       else if (hcodec->patch_ops.free)
                hcodec->patch_ops.free(hcodec);
 error_regmap:
        snd_hdac_regmap_exit(hdev);
@@ -548,6 +559,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
                      snd_soc_component_get_drvdata(component);
        struct hdac_device *hdev = &hda_pvt->codec->core;
        struct hda_codec *codec = hda_pvt->codec;
+       struct hda_codec_driver *driver = hda_codec_to_driver(codec);
        struct hdac_ext_link *hlink = NULL;
 
        hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
@@ -559,7 +571,9 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
        pm_runtime_disable(&hdev->dev);
        snd_hdac_ext_bus_link_put(hdev->bus, hlink);
 
-       if (codec->patch_ops.free)
+       if (driver->ops && driver->ops->remove)
+               driver->ops->remove(codec);
+       else if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
 
        snd_hda_codec_cleanup_for_unbind(codec);