18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HD-audio codec driver binding 48c2ecf20Sopenharmony_ci * Copyright (c) Takashi Iwai <tiwai@suse.de> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/mutex.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <linux/pm.h> 138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 168c2ecf20Sopenharmony_ci#include "hda_local.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * find a matching codec id 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_cistatic int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 248c2ecf20Sopenharmony_ci struct hda_codec_driver *driver = 258c2ecf20Sopenharmony_ci container_of(drv, struct hda_codec_driver, core); 268c2ecf20Sopenharmony_ci const struct hda_device_id *list; 278c2ecf20Sopenharmony_ci /* check probe_id instead of vendor_id if set */ 288c2ecf20Sopenharmony_ci u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; 298c2ecf20Sopenharmony_ci u32 rev_id = codec->core.revision_id; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci for (list = driver->id; list->vendor_id; list++) { 328c2ecf20Sopenharmony_ci if (list->vendor_id == id && 338c2ecf20Sopenharmony_ci (!list->rev_id || list->rev_id == rev_id)) { 348c2ecf20Sopenharmony_ci codec->preset = list; 358c2ecf20Sopenharmony_ci return 1; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* process an unsolicited event */ 428c2ecf20Sopenharmony_cistatic void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct hda_codec *codec = container_of(dev, struct hda_codec, core); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* ignore unsol events during shutdown */ 478c2ecf20Sopenharmony_ci if (codec->bus->shutdown) 488c2ecf20Sopenharmony_ci return; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* ignore unsol events during system suspend/resume */ 518c2ecf20Sopenharmony_ci if (codec->core.dev.power.power_state.event != PM_EVENT_ON) 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (codec->patch_ops.unsol_event) 558c2ecf20Sopenharmony_ci codec->patch_ops.unsol_event(codec, ev); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * snd_hda_codec_set_name - set the codec name 608c2ecf20Sopenharmony_ci * @codec: the HDA codec 618c2ecf20Sopenharmony_ci * @name: name string to set 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ciint snd_hda_codec_set_name(struct hda_codec *codec, const char *name) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (!name) 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci err = snd_hdac_device_set_chip_name(&codec->core, name); 708c2ecf20Sopenharmony_ci if (err < 0) 718c2ecf20Sopenharmony_ci return err; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* update the mixer name */ 748c2ecf20Sopenharmony_ci if (!*codec->card->mixername || 758c2ecf20Sopenharmony_ci codec->bus->mixer_assigned >= codec->core.addr) { 768c2ecf20Sopenharmony_ci snprintf(codec->card->mixername, 778c2ecf20Sopenharmony_ci sizeof(codec->card->mixername), "%s %s", 788c2ecf20Sopenharmony_ci codec->core.vendor_name, codec->core.chip_name); 798c2ecf20Sopenharmony_ci codec->bus->mixer_assigned = codec->core.addr; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_set_name); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int hda_codec_driver_probe(struct device *dev) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 898c2ecf20Sopenharmony_ci struct module *owner = dev->driver->owner; 908c2ecf20Sopenharmony_ci hda_codec_patch_t patch; 918c2ecf20Sopenharmony_ci int err; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (codec->bus->core.ext_ops) { 948c2ecf20Sopenharmony_ci if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach)) 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci return codec->bus->core.ext_ops->hdev_attach(&codec->core); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (WARN_ON(!codec->preset)) 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci err = snd_hda_codec_set_name(codec, codec->preset->name); 1038c2ecf20Sopenharmony_ci if (err < 0) 1048c2ecf20Sopenharmony_ci goto error; 1058c2ecf20Sopenharmony_ci err = snd_hdac_regmap_init(&codec->core); 1068c2ecf20Sopenharmony_ci if (err < 0) 1078c2ecf20Sopenharmony_ci goto error; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!try_module_get(owner)) { 1108c2ecf20Sopenharmony_ci err = -EINVAL; 1118c2ecf20Sopenharmony_ci goto error; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci patch = (hda_codec_patch_t)codec->preset->driver_data; 1158c2ecf20Sopenharmony_ci if (patch) { 1168c2ecf20Sopenharmony_ci err = patch(codec); 1178c2ecf20Sopenharmony_ci if (err < 0) 1188c2ecf20Sopenharmony_ci goto error_module_put; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err = snd_hda_codec_build_pcms(codec); 1228c2ecf20Sopenharmony_ci if (err < 0) 1238c2ecf20Sopenharmony_ci goto error_module; 1248c2ecf20Sopenharmony_ci err = snd_hda_codec_build_controls(codec); 1258c2ecf20Sopenharmony_ci if (err < 0) 1268c2ecf20Sopenharmony_ci goto error_module; 1278c2ecf20Sopenharmony_ci /* only register after the bus probe finished; otherwise it's racy */ 1288c2ecf20Sopenharmony_ci if (!codec->bus->bus_probing && codec->card->registered) { 1298c2ecf20Sopenharmony_ci err = snd_card_register(codec->card); 1308c2ecf20Sopenharmony_ci if (err < 0) 1318c2ecf20Sopenharmony_ci goto error_module; 1328c2ecf20Sopenharmony_ci snd_hda_codec_register(codec); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci codec->core.lazy_cache = true; 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci error_module: 1398c2ecf20Sopenharmony_ci if (codec->patch_ops.free) 1408c2ecf20Sopenharmony_ci codec->patch_ops.free(codec); 1418c2ecf20Sopenharmony_ci error_module_put: 1428c2ecf20Sopenharmony_ci module_put(owner); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci error: 1458c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_for_unbind(codec); 1468c2ecf20Sopenharmony_ci codec->preset = NULL; 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int hda_codec_driver_remove(struct device *dev) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (codec->bus->core.ext_ops) { 1558c2ecf20Sopenharmony_ci if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) 1568c2ecf20Sopenharmony_ci return -EINVAL; 1578c2ecf20Sopenharmony_ci return codec->bus->core.ext_ops->hdev_detach(&codec->core); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (codec->patch_ops.free) 1618c2ecf20Sopenharmony_ci codec->patch_ops.free(codec); 1628c2ecf20Sopenharmony_ci snd_hda_codec_cleanup_for_unbind(codec); 1638c2ecf20Sopenharmony_ci codec->preset = NULL; 1648c2ecf20Sopenharmony_ci module_put(dev->driver->owner); 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void hda_codec_driver_shutdown(struct device *dev) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct hda_codec *codec = dev_to_hda_codec(dev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev) && codec->patch_ops.reboot_notify) 1738c2ecf20Sopenharmony_ci codec->patch_ops.reboot_notify(codec); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciint __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, 1778c2ecf20Sopenharmony_ci struct module *owner) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci drv->core.driver.name = name; 1808c2ecf20Sopenharmony_ci drv->core.driver.owner = owner; 1818c2ecf20Sopenharmony_ci drv->core.driver.bus = &snd_hda_bus_type; 1828c2ecf20Sopenharmony_ci drv->core.driver.probe = hda_codec_driver_probe; 1838c2ecf20Sopenharmony_ci drv->core.driver.remove = hda_codec_driver_remove; 1848c2ecf20Sopenharmony_ci drv->core.driver.shutdown = hda_codec_driver_shutdown; 1858c2ecf20Sopenharmony_ci drv->core.driver.pm = &hda_codec_driver_pm; 1868c2ecf20Sopenharmony_ci drv->core.type = HDA_DEV_LEGACY; 1878c2ecf20Sopenharmony_ci drv->core.match = hda_codec_match; 1888c2ecf20Sopenharmony_ci drv->core.unsol_event = hda_codec_unsol_event; 1898c2ecf20Sopenharmony_ci return driver_register(&drv->core.driver); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__hda_codec_driver_register); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_civoid hda_codec_driver_unregister(struct hda_codec_driver *drv) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci driver_unregister(&drv->core.driver); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hda_codec_driver_unregister); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic inline bool codec_probed(struct hda_codec *codec) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* try to auto-load codec module */ 2058c2ecf20Sopenharmony_cistatic void request_codec_module(struct hda_codec *codec) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci#ifdef MODULE 2088c2ecf20Sopenharmony_ci char modalias[32]; 2098c2ecf20Sopenharmony_ci const char *mod = NULL; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci switch (codec->probe_id) { 2128c2ecf20Sopenharmony_ci case HDA_CODEC_ID_GENERIC_HDMI: 2138c2ecf20Sopenharmony_ci#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) 2148c2ecf20Sopenharmony_ci mod = "snd-hda-codec-hdmi"; 2158c2ecf20Sopenharmony_ci#endif 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci case HDA_CODEC_ID_GENERIC: 2188c2ecf20Sopenharmony_ci#if IS_MODULE(CONFIG_SND_HDA_GENERIC) 2198c2ecf20Sopenharmony_ci mod = "snd-hda-codec-generic"; 2208c2ecf20Sopenharmony_ci#endif 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci default: 2238c2ecf20Sopenharmony_ci snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); 2248c2ecf20Sopenharmony_ci mod = modalias; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (mod) 2298c2ecf20Sopenharmony_ci request_module(mod); 2308c2ecf20Sopenharmony_ci#endif /* MODULE */ 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* try to auto-load and bind the codec module */ 2348c2ecf20Sopenharmony_cistatic void codec_bind_module(struct hda_codec *codec) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci#ifdef MODULE 2378c2ecf20Sopenharmony_ci request_codec_module(codec); 2388c2ecf20Sopenharmony_ci if (codec_probed(codec)) 2398c2ecf20Sopenharmony_ci return; 2408c2ecf20Sopenharmony_ci#endif 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) 2448c2ecf20Sopenharmony_ci/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ 2458c2ecf20Sopenharmony_cistatic bool is_likely_hdmi_codec(struct hda_codec *codec) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci hda_nid_t nid; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for_each_hda_codec_node(nid, codec) { 2508c2ecf20Sopenharmony_ci unsigned int wcaps = get_wcaps(codec, nid); 2518c2ecf20Sopenharmony_ci switch (get_wcaps_type(wcaps)) { 2528c2ecf20Sopenharmony_ci case AC_WID_AUD_IN: 2538c2ecf20Sopenharmony_ci return false; /* HDMI parser supports only HDMI out */ 2548c2ecf20Sopenharmony_ci case AC_WID_AUD_OUT: 2558c2ecf20Sopenharmony_ci if (!(wcaps & AC_WCAP_DIGITAL)) 2568c2ecf20Sopenharmony_ci return false; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci return true; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci#else 2638c2ecf20Sopenharmony_ci/* no HDMI codec parser support */ 2648c2ecf20Sopenharmony_ci#define is_likely_hdmi_codec(codec) false 2658c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_HDA_CODEC_HDMI */ 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int codec_bind_generic(struct hda_codec *codec) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci if (codec->probe_id) 2708c2ecf20Sopenharmony_ci return -ENODEV; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (is_likely_hdmi_codec(codec)) { 2738c2ecf20Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; 2748c2ecf20Sopenharmony_ci request_codec_module(codec); 2758c2ecf20Sopenharmony_ci if (codec_probed(codec)) 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 2808c2ecf20Sopenharmony_ci request_codec_module(codec); 2818c2ecf20Sopenharmony_ci if (codec_probed(codec)) 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci return -ENODEV; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) 2878c2ecf20Sopenharmony_ci#define is_generic_config(codec) \ 2888c2ecf20Sopenharmony_ci (codec->modelname && !strcmp(codec->modelname, "generic")) 2898c2ecf20Sopenharmony_ci#else 2908c2ecf20Sopenharmony_ci#define is_generic_config(codec) 0 2918c2ecf20Sopenharmony_ci#endif 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/** 2948c2ecf20Sopenharmony_ci * snd_hda_codec_configure - (Re-)configure the HD-audio codec 2958c2ecf20Sopenharmony_ci * @codec: the HDA codec 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * Start parsing of the given codec tree and (re-)initialize the whole 2988c2ecf20Sopenharmony_ci * patch instance. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Returns 0 if successful or a negative error code. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ciint snd_hda_codec_configure(struct hda_codec *codec) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int err; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (codec->configured) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (is_generic_config(codec)) 3108c2ecf20Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 3118c2ecf20Sopenharmony_ci else 3128c2ecf20Sopenharmony_ci codec->probe_id = 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (!device_is_registered(&codec->core.dev)) { 3158c2ecf20Sopenharmony_ci err = snd_hdac_device_register(&codec->core); 3168c2ecf20Sopenharmony_ci if (err < 0) 3178c2ecf20Sopenharmony_ci return err; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!codec->preset) 3218c2ecf20Sopenharmony_ci codec_bind_module(codec); 3228c2ecf20Sopenharmony_ci if (!codec->preset) { 3238c2ecf20Sopenharmony_ci err = codec_bind_generic(codec); 3248c2ecf20Sopenharmony_ci if (err < 0) { 3258c2ecf20Sopenharmony_ci codec_dbg(codec, "Unable to bind the codec\n"); 3268c2ecf20Sopenharmony_ci return err; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci codec->configured = 1; 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hda_codec_configure); 334