162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HD-audio codec driver binding 462306a36Sopenharmony_ci * Copyright (c) Takashi Iwai <tiwai@suse.de> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/mutex.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/pm.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci#include <sound/hda_codec.h> 1562306a36Sopenharmony_ci#include "hda_local.h" 1662306a36Sopenharmony_ci#include "hda_jack.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * find a matching codec id 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_cistatic int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 2462306a36Sopenharmony_ci struct hda_codec_driver *driver = 2562306a36Sopenharmony_ci container_of(drv, struct hda_codec_driver, core); 2662306a36Sopenharmony_ci const struct hda_device_id *list; 2762306a36Sopenharmony_ci /* check probe_id instead of vendor_id if set */ 2862306a36Sopenharmony_ci u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; 2962306a36Sopenharmony_ci u32 rev_id = codec->core.revision_id; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci for (list = driver->id; list->vendor_id; list++) { 3262306a36Sopenharmony_ci if (list->vendor_id == id && 3362306a36Sopenharmony_ci (!list->rev_id || list->rev_id == rev_id)) { 3462306a36Sopenharmony_ci codec->preset = list; 3562306a36Sopenharmony_ci return 1; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* process an unsolicited event */ 4262306a36Sopenharmony_cistatic void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* ignore unsol events during shutdown */ 4762306a36Sopenharmony_ci if (codec->bus->shutdown) 4862306a36Sopenharmony_ci return; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* ignore unsol events during system suspend/resume */ 5162306a36Sopenharmony_ci if (codec->core.dev.power.power_state.event != PM_EVENT_ON) 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (codec->patch_ops.unsol_event) 5562306a36Sopenharmony_ci codec->patch_ops.unsol_event(codec, ev); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/** 5962306a36Sopenharmony_ci * snd_hda_codec_set_name - set the codec name 6062306a36Sopenharmony_ci * @codec: the HDA codec 6162306a36Sopenharmony_ci * @name: name string to set 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ciint snd_hda_codec_set_name(struct hda_codec *codec, const char *name) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int err; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!name) 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci err = snd_hdac_device_set_chip_name(&codec->core, name); 7062306a36Sopenharmony_ci if (err < 0) 7162306a36Sopenharmony_ci return err; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* update the mixer name */ 7462306a36Sopenharmony_ci if (!*codec->card->mixername || 7562306a36Sopenharmony_ci codec->bus->mixer_assigned >= codec->core.addr) { 7662306a36Sopenharmony_ci snprintf(codec->card->mixername, 7762306a36Sopenharmony_ci sizeof(codec->card->mixername), "%s %s", 7862306a36Sopenharmony_ci codec->core.vendor_name, codec->core.chip_name); 7962306a36Sopenharmony_ci codec->bus->mixer_assigned = codec->core.addr; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_set_name); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int hda_codec_driver_probe(struct device *dev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 8962306a36Sopenharmony_ci struct module *owner = dev->driver->owner; 9062306a36Sopenharmony_ci hda_codec_patch_t patch; 9162306a36Sopenharmony_ci int err; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (codec->bus->core.ext_ops) { 9462306a36Sopenharmony_ci if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach)) 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci return codec->bus->core.ext_ops->hdev_attach(&codec->core); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (WARN_ON(!codec->preset)) 10062306a36Sopenharmony_ci return -EINVAL; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci err = snd_hda_codec_set_name(codec, codec->preset->name); 10362306a36Sopenharmony_ci if (err < 0) 10462306a36Sopenharmony_ci goto error; 10562306a36Sopenharmony_ci err = snd_hdac_regmap_init(&codec->core); 10662306a36Sopenharmony_ci if (err < 0) 10762306a36Sopenharmony_ci goto error; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!try_module_get(owner)) { 11062306a36Sopenharmony_ci err = -EINVAL; 11162306a36Sopenharmony_ci goto error; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci patch = (hda_codec_patch_t)codec->preset->driver_data; 11562306a36Sopenharmony_ci if (patch) { 11662306a36Sopenharmony_ci err = patch(codec); 11762306a36Sopenharmony_ci if (err < 0) 11862306a36Sopenharmony_ci goto error_module_put; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci err = snd_hda_codec_build_pcms(codec); 12262306a36Sopenharmony_ci if (err < 0) 12362306a36Sopenharmony_ci goto error_module; 12462306a36Sopenharmony_ci err = snd_hda_codec_build_controls(codec); 12562306a36Sopenharmony_ci if (err < 0) 12662306a36Sopenharmony_ci goto error_module; 12762306a36Sopenharmony_ci /* only register after the bus probe finished; otherwise it's racy */ 12862306a36Sopenharmony_ci if (!codec->bus->bus_probing && codec->card->registered) { 12962306a36Sopenharmony_ci err = snd_card_register(codec->card); 13062306a36Sopenharmony_ci if (err < 0) 13162306a36Sopenharmony_ci goto error_module; 13262306a36Sopenharmony_ci snd_hda_codec_register(codec); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci codec->core.lazy_cache = true; 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci error_module: 13962306a36Sopenharmony_ci if (codec->patch_ops.free) 14062306a36Sopenharmony_ci codec->patch_ops.free(codec); 14162306a36Sopenharmony_ci error_module_put: 14262306a36Sopenharmony_ci module_put(owner); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci error: 14562306a36Sopenharmony_ci snd_hda_codec_cleanup_for_unbind(codec); 14662306a36Sopenharmony_ci codec->preset = NULL; 14762306a36Sopenharmony_ci return err; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int hda_codec_driver_remove(struct device *dev) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (codec->bus->core.ext_ops) { 15562306a36Sopenharmony_ci if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci return codec->bus->core.ext_ops->hdev_detach(&codec->core); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci snd_hda_codec_disconnect_pcms(codec); 16162306a36Sopenharmony_ci snd_hda_jack_tbl_disconnect(codec); 16262306a36Sopenharmony_ci if (!refcount_dec_and_test(&codec->pcm_ref)) 16362306a36Sopenharmony_ci wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); 16462306a36Sopenharmony_ci snd_power_sync_ref(codec->bus->card); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (codec->patch_ops.free) 16762306a36Sopenharmony_ci codec->patch_ops.free(codec); 16862306a36Sopenharmony_ci snd_hda_codec_cleanup_for_unbind(codec); 16962306a36Sopenharmony_ci codec->preset = NULL; 17062306a36Sopenharmony_ci module_put(dev->driver->owner); 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void hda_codec_driver_shutdown(struct device *dev) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci snd_hda_codec_shutdown(dev_to_hda_codec(dev)); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciint __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, 18062306a36Sopenharmony_ci struct module *owner) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci drv->core.driver.name = name; 18362306a36Sopenharmony_ci drv->core.driver.owner = owner; 18462306a36Sopenharmony_ci drv->core.driver.bus = &snd_hda_bus_type; 18562306a36Sopenharmony_ci drv->core.driver.probe = hda_codec_driver_probe; 18662306a36Sopenharmony_ci drv->core.driver.remove = hda_codec_driver_remove; 18762306a36Sopenharmony_ci drv->core.driver.shutdown = hda_codec_driver_shutdown; 18862306a36Sopenharmony_ci drv->core.driver.pm = &hda_codec_driver_pm; 18962306a36Sopenharmony_ci drv->core.type = HDA_DEV_LEGACY; 19062306a36Sopenharmony_ci drv->core.match = hda_codec_match; 19162306a36Sopenharmony_ci drv->core.unsol_event = hda_codec_unsol_event; 19262306a36Sopenharmony_ci return driver_register(&drv->core.driver); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__hda_codec_driver_register); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid hda_codec_driver_unregister(struct hda_codec_driver *drv) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci driver_unregister(&drv->core.driver); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_codec_driver_unregister); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic inline bool codec_probed(struct hda_codec *codec) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* try to auto-load codec module */ 20862306a36Sopenharmony_cistatic void request_codec_module(struct hda_codec *codec) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci#ifdef MODULE 21162306a36Sopenharmony_ci char modalias[32]; 21262306a36Sopenharmony_ci const char *mod = NULL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (codec->probe_id) { 21562306a36Sopenharmony_ci case HDA_CODEC_ID_GENERIC_HDMI: 21662306a36Sopenharmony_ci#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) 21762306a36Sopenharmony_ci mod = "snd-hda-codec-hdmi"; 21862306a36Sopenharmony_ci#endif 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci case HDA_CODEC_ID_GENERIC: 22162306a36Sopenharmony_ci#if IS_MODULE(CONFIG_SND_HDA_GENERIC) 22262306a36Sopenharmony_ci mod = "snd-hda-codec-generic"; 22362306a36Sopenharmony_ci#endif 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci default: 22662306a36Sopenharmony_ci snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); 22762306a36Sopenharmony_ci mod = modalias; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (mod) 23262306a36Sopenharmony_ci request_module(mod); 23362306a36Sopenharmony_ci#endif /* MODULE */ 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* try to auto-load and bind the codec module */ 23762306a36Sopenharmony_cistatic void codec_bind_module(struct hda_codec *codec) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci#ifdef MODULE 24062306a36Sopenharmony_ci request_codec_module(codec); 24162306a36Sopenharmony_ci if (codec_probed(codec)) 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci#endif 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) 24762306a36Sopenharmony_ci/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ 24862306a36Sopenharmony_cistatic bool is_likely_hdmi_codec(struct hda_codec *codec) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci hda_nid_t nid; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * For ASoC users, if snd_hda_hdmi_codec module is denylisted and any 25462306a36Sopenharmony_ci * event causes i915 enumeration to fail, ->wcaps remains uninitialized. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci if (!codec->wcaps) 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 26062306a36Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 26162306a36Sopenharmony_ci switch (get_wcaps_type(wcaps)) { 26262306a36Sopenharmony_ci case AC_WID_AUD_IN: 26362306a36Sopenharmony_ci return false; /* HDMI parser supports only HDMI out */ 26462306a36Sopenharmony_ci case AC_WID_AUD_OUT: 26562306a36Sopenharmony_ci if (!(wcaps & AC_WCAP_DIGITAL)) 26662306a36Sopenharmony_ci return false; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci return true; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci#else 27362306a36Sopenharmony_ci/* no HDMI codec parser support */ 27462306a36Sopenharmony_ci#define is_likely_hdmi_codec(codec) false 27562306a36Sopenharmony_ci#endif /* CONFIG_SND_HDA_CODEC_HDMI */ 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int codec_bind_generic(struct hda_codec *codec) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci if (codec->probe_id) 28062306a36Sopenharmony_ci return -ENODEV; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (is_likely_hdmi_codec(codec)) { 28362306a36Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; 28462306a36Sopenharmony_ci request_codec_module(codec); 28562306a36Sopenharmony_ci if (codec_probed(codec)) 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 29062306a36Sopenharmony_ci request_codec_module(codec); 29162306a36Sopenharmony_ci if (codec_probed(codec)) 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci return -ENODEV; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) 29762306a36Sopenharmony_ci#define is_generic_config(codec) \ 29862306a36Sopenharmony_ci (codec->modelname && !strcmp(codec->modelname, "generic")) 29962306a36Sopenharmony_ci#else 30062306a36Sopenharmony_ci#define is_generic_config(codec) 0 30162306a36Sopenharmony_ci#endif 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/** 30462306a36Sopenharmony_ci * snd_hda_codec_configure - (Re-)configure the HD-audio codec 30562306a36Sopenharmony_ci * @codec: the HDA codec 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * Start parsing of the given codec tree and (re-)initialize the whole 30862306a36Sopenharmony_ci * patch instance. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * Returns 0 if successful or a negative error code. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ciint snd_hda_codec_configure(struct hda_codec *codec) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int err; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (codec->configured) 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (is_generic_config(codec)) 32062306a36Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 32162306a36Sopenharmony_ci else 32262306a36Sopenharmony_ci codec->probe_id = 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!device_is_registered(&codec->core.dev)) { 32562306a36Sopenharmony_ci err = snd_hdac_device_register(&codec->core); 32662306a36Sopenharmony_ci if (err < 0) 32762306a36Sopenharmony_ci return err; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!codec->preset) 33162306a36Sopenharmony_ci codec_bind_module(codec); 33262306a36Sopenharmony_ci if (!codec->preset) { 33362306a36Sopenharmony_ci err = codec_bind_generic(codec); 33462306a36Sopenharmony_ci if (err < 0) { 33562306a36Sopenharmony_ci codec_dbg(codec, "Unable to bind the codec\n"); 33662306a36Sopenharmony_ci return err; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci codec->configured = 1; 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_configure); 344