18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Authors: Keyon Jie <yang.jie@linux.intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <sound/hdaudio_ext.h> 138c2ecf20Sopenharmony_ci#include <sound/hda_register.h> 148c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 158c2ecf20Sopenharmony_ci#include <sound/hda_i915.h> 168c2ecf20Sopenharmony_ci#include <sound/sof.h> 178c2ecf20Sopenharmony_ci#include "../ops.h" 188c2ecf20Sopenharmony_ci#include "hda.h" 198c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 208c2ecf20Sopenharmony_ci#include "../../codecs/hdac_hda.h" 218c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 248c2ecf20Sopenharmony_ci#define IDISP_VID_INTEL 0x80860000 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* load the legacy HDA codec driver */ 278c2ecf20Sopenharmony_cistatic int request_codec_module(struct hda_codec *codec) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci#ifdef MODULE 308c2ecf20Sopenharmony_ci char alias[MODULE_NAME_LEN]; 318c2ecf20Sopenharmony_ci const char *mod = NULL; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci switch (codec->probe_id) { 348c2ecf20Sopenharmony_ci case HDA_CODEC_ID_GENERIC: 358c2ecf20Sopenharmony_ci#if IS_MODULE(CONFIG_SND_HDA_GENERIC) 368c2ecf20Sopenharmony_ci mod = "snd-hda-codec-generic"; 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci break; 398c2ecf20Sopenharmony_ci default: 408c2ecf20Sopenharmony_ci snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias)); 418c2ecf20Sopenharmony_ci mod = alias; 428c2ecf20Sopenharmony_ci break; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (mod) { 468c2ecf20Sopenharmony_ci dev_dbg(&codec->core.dev, "loading codec module: %s\n", mod); 478c2ecf20Sopenharmony_ci request_module(mod); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci#endif /* MODULE */ 508c2ecf20Sopenharmony_ci return device_attach(hda_codec_dev(codec)); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int hda_codec_load_module(struct hda_codec *codec) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int ret = request_codec_module(codec); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (ret <= 0) { 588c2ecf20Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 598c2ecf20Sopenharmony_ci ret = request_codec_module(codec); 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* enable controller wake up event for all codecs with jack connectors */ 668c2ecf20Sopenharmony_civoid hda_codec_jack_wake_enable(struct snd_sof_dev *sdev) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct hda_bus *hbus = sof_to_hbus(sdev); 698c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 708c2ecf20Sopenharmony_ci struct hda_codec *codec; 718c2ecf20Sopenharmony_ci unsigned int mask = 0; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci list_for_each_codec(codec, hbus) 748c2ecf20Sopenharmony_ci if (codec->jacktbl.used) 758c2ecf20Sopenharmony_ci mask |= BIT(codec->core.addr); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* check jack status after resuming from suspend mode */ 818c2ecf20Sopenharmony_civoid hda_codec_jack_check(struct snd_sof_dev *sdev) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct hda_bus *hbus = sof_to_hbus(sdev); 848c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 858c2ecf20Sopenharmony_ci struct hda_codec *codec; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* disable controller Wake Up event*/ 888c2ecf20Sopenharmony_ci snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci list_for_each_codec(codec, hbus) 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * Wake up all jack-detecting codecs regardless whether an event 938c2ecf20Sopenharmony_ci * has been recorded in STATESTS 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci if (codec->jacktbl.used) 968c2ecf20Sopenharmony_ci pm_request_resume(&codec->core.dev); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci#else 998c2ecf20Sopenharmony_civoid hda_codec_jack_wake_enable(struct snd_sof_dev *sdev) {} 1008c2ecf20Sopenharmony_civoid hda_codec_jack_check(struct snd_sof_dev *sdev) {} 1018c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ 1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(hda_codec_jack_wake_enable, SND_SOC_SOF_HDA_AUDIO_CODEC); 1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(hda_codec_jack_check, SND_SOC_SOF_HDA_AUDIO_CODEC); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) 1068c2ecf20Sopenharmony_ci#define is_generic_config(bus) \ 1078c2ecf20Sopenharmony_ci ((bus)->modelname && !strcmp((bus)->modelname, "generic")) 1088c2ecf20Sopenharmony_ci#else 1098c2ecf20Sopenharmony_ci#define is_generic_config(x) 0 1108c2ecf20Sopenharmony_ci#endif 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* probe individual codec */ 1138c2ecf20Sopenharmony_cistatic int hda_codec_probe(struct snd_sof_dev *sdev, int address, 1148c2ecf20Sopenharmony_ci bool hda_codec_use_common_hdmi) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 1178c2ecf20Sopenharmony_ci struct hdac_hda_priv *hda_priv; 1188c2ecf20Sopenharmony_ci struct hda_codec *codec; 1198c2ecf20Sopenharmony_ci int type = HDA_DEV_LEGACY; 1208c2ecf20Sopenharmony_ci#endif 1218c2ecf20Sopenharmony_ci struct hda_bus *hbus = sof_to_hbus(sdev); 1228c2ecf20Sopenharmony_ci struct hdac_device *hdev; 1238c2ecf20Sopenharmony_ci u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | 1248c2ecf20Sopenharmony_ci (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; 1258c2ecf20Sopenharmony_ci u32 resp = -1; 1268c2ecf20Sopenharmony_ci int ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci mutex_lock(&hbus->core.cmd_mutex); 1298c2ecf20Sopenharmony_ci snd_hdac_bus_send_cmd(&hbus->core, hda_cmd); 1308c2ecf20Sopenharmony_ci snd_hdac_bus_get_response(&hbus->core, address, &resp); 1318c2ecf20Sopenharmony_ci mutex_unlock(&hbus->core.cmd_mutex); 1328c2ecf20Sopenharmony_ci if (resp == -1) 1338c2ecf20Sopenharmony_ci return -EIO; 1348c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n", 1358c2ecf20Sopenharmony_ci address, resp); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 1388c2ecf20Sopenharmony_ci hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL); 1398c2ecf20Sopenharmony_ci if (!hda_priv) 1408c2ecf20Sopenharmony_ci return -ENOMEM; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci hda_priv->codec.bus = hbus; 1438c2ecf20Sopenharmony_ci hdev = &hda_priv->codec.core; 1448c2ecf20Sopenharmony_ci codec = &hda_priv->codec; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* only probe ASoC codec drivers for HDAC-HDMI */ 1478c2ecf20Sopenharmony_ci if (!hda_codec_use_common_hdmi && (resp & 0xFFFF0000) == IDISP_VID_INTEL) 1488c2ecf20Sopenharmony_ci type = HDA_DEV_ASOC; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, type); 1518c2ecf20Sopenharmony_ci if (ret < 0) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) { 1558c2ecf20Sopenharmony_ci if (!hdev->bus->audio_component) { 1568c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, 1578c2ecf20Sopenharmony_ci "iDisp hw present but no driver\n"); 1588c2ecf20Sopenharmony_ci ret = -ENOENT; 1598c2ecf20Sopenharmony_ci goto out; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci hda_priv->need_display_power = true; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (is_generic_config(hbus)) 1658c2ecf20Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci codec->probe_id = 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (type == HDA_DEV_LEGACY) { 1708c2ecf20Sopenharmony_ci ret = hda_codec_load_module(codec); 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * handle ret==0 (no driver bound) as an error, but pass 1738c2ecf20Sopenharmony_ci * other return codes without modification 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci if (ret == 0) 1768c2ecf20Sopenharmony_ci ret = -ENOENT; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciout: 1808c2ecf20Sopenharmony_ci if (ret < 0) { 1818c2ecf20Sopenharmony_ci snd_hdac_device_unregister(hdev); 1828c2ecf20Sopenharmony_ci put_device(&hdev->dev); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci#else 1858c2ecf20Sopenharmony_ci hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL); 1868c2ecf20Sopenharmony_ci if (!hdev) 1878c2ecf20Sopenharmony_ci return -ENOMEM; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, HDA_DEV_ASOC); 1908c2ecf20Sopenharmony_ci#endif 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* Codec initialization */ 1968c2ecf20Sopenharmony_civoid hda_codec_probe_bus(struct snd_sof_dev *sdev, 1978c2ecf20Sopenharmony_ci bool hda_codec_use_common_hdmi) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 2008c2ecf20Sopenharmony_ci int i, ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* probe codecs in avail slots */ 2038c2ecf20Sopenharmony_ci for (i = 0; i < HDA_MAX_CODECS; i++) { 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!(bus->codec_mask & (1 << i))) 2068c2ecf20Sopenharmony_ci continue; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = hda_codec_probe(sdev, i, hda_codec_use_common_hdmi); 2098c2ecf20Sopenharmony_ci if (ret < 0) { 2108c2ecf20Sopenharmony_ci dev_warn(bus->dev, "codec #%d probe error, ret: %d\n", 2118c2ecf20Sopenharmony_ci i, ret); 2128c2ecf20Sopenharmony_ci bus->codec_mask &= ~BIT(i); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(hda_codec_probe_bus, SND_SOC_SOF_HDA_AUDIO_CODEC); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) || \ 2198c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_civoid hda_codec_i915_display_power(struct snd_sof_dev *sdev, bool enable) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (HDA_IDISP_CODEC(bus->codec_mask)) { 2268c2ecf20Sopenharmony_ci dev_dbg(bus->dev, "Turning i915 HDAC power %d\n", enable); 2278c2ecf20Sopenharmony_ci snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, enable); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(hda_codec_i915_display_power, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciint hda_codec_i915_init(struct snd_sof_dev *sdev) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* i915 exposes a HDA codec for HDMI audio */ 2388c2ecf20Sopenharmony_ci ret = snd_hdac_i915_init(bus); 2398c2ecf20Sopenharmony_ci if (ret < 0) 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* codec_mask not yet known, power up for probe */ 2438c2ecf20Sopenharmony_ci snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(hda_codec_i915_init, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ciint hda_codec_i915_exit(struct snd_sof_dev *sdev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!bus->audio_component) 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* power down unconditionally */ 2578c2ecf20Sopenharmony_ci snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return snd_hdac_i915_exit(bus); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_NS(hda_codec_i915_exit, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#endif 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 266