162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Authors: Keyon Jie <yang.jie@linux.intel.com> 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 1062306a36Sopenharmony_ci#include <sound/hda_register.h> 1162306a36Sopenharmony_ci#include <sound/hda_codec.h> 1262306a36Sopenharmony_ci#include <sound/hda_i915.h> 1362306a36Sopenharmony_ci#include <sound/sof.h> 1462306a36Sopenharmony_ci#include "../ops.h" 1562306a36Sopenharmony_ci#include "hda.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 1862306a36Sopenharmony_ci#include "../../codecs/hdac_hda.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define CODEC_PROBE_RETRIES 3 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define IDISP_VID_INTEL 0x80860000 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int hda_codec_mask = -1; 2562306a36Sopenharmony_cimodule_param_named(codec_mask, hda_codec_mask, int, 0444); 2662306a36Sopenharmony_ciMODULE_PARM_DESC(codec_mask, "SOF HDA codec mask for probing"); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* load the legacy HDA codec driver */ 2962306a36Sopenharmony_cistatic int request_codec_module(struct hda_codec *codec) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci#ifdef MODULE 3262306a36Sopenharmony_ci char alias[MODULE_NAME_LEN]; 3362306a36Sopenharmony_ci const char *mod = NULL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci switch (codec->probe_id) { 3662306a36Sopenharmony_ci case HDA_CODEC_ID_GENERIC: 3762306a36Sopenharmony_ci#if IS_MODULE(CONFIG_SND_HDA_GENERIC) 3862306a36Sopenharmony_ci mod = "snd-hda-codec-generic"; 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci default: 4262306a36Sopenharmony_ci snd_hdac_codec_modalias(&codec->core, alias, sizeof(alias)); 4362306a36Sopenharmony_ci mod = alias; 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (mod) { 4862306a36Sopenharmony_ci dev_dbg(&codec->core.dev, "loading codec module: %s\n", mod); 4962306a36Sopenharmony_ci request_module(mod); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci#endif /* MODULE */ 5262306a36Sopenharmony_ci return device_attach(hda_codec_dev(codec)); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int hda_codec_load_module(struct hda_codec *codec) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int ret; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ret = snd_hdac_device_register(&codec->core); 6062306a36Sopenharmony_ci if (ret) { 6162306a36Sopenharmony_ci dev_err(&codec->core.dev, "failed to register hdac device\n"); 6262306a36Sopenharmony_ci put_device(&codec->core.dev); 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ret = request_codec_module(codec); 6762306a36Sopenharmony_ci if (ret <= 0) { 6862306a36Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 6962306a36Sopenharmony_ci ret = request_codec_module(codec); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return ret; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* enable controller wake up event for all codecs with jack connectors */ 7662306a36Sopenharmony_civoid hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct hda_bus *hbus = sof_to_hbus(sdev); 7962306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 8062306a36Sopenharmony_ci struct hda_codec *codec; 8162306a36Sopenharmony_ci unsigned int mask = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 8462306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 8562306a36Sopenharmony_ci return; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (enable) { 8862306a36Sopenharmony_ci list_for_each_codec(codec, hbus) 8962306a36Sopenharmony_ci if (codec->jacktbl.used) 9062306a36Sopenharmony_ci mask |= BIT(codec->core.addr); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_jack_wake_enable, SND_SOC_SOF_HDA_AUDIO_CODEC); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* check jack status after resuming from suspend mode */ 9862306a36Sopenharmony_civoid hda_codec_jack_check(struct snd_sof_dev *sdev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct hda_bus *hbus = sof_to_hbus(sdev); 10162306a36Sopenharmony_ci struct hda_codec *codec; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 10462306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 10562306a36Sopenharmony_ci return; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci list_for_each_codec(codec, hbus) 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * Wake up all jack-detecting codecs regardless whether an event 11062306a36Sopenharmony_ci * has been recorded in STATESTS 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci if (codec->jacktbl.used) 11362306a36Sopenharmony_ci pm_request_resume(&codec->core.dev); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_jack_check, SND_SOC_SOF_HDA_AUDIO_CODEC); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) 11862306a36Sopenharmony_ci#define is_generic_config(bus) \ 11962306a36Sopenharmony_ci ((bus)->modelname && !strcmp((bus)->modelname, "generic")) 12062306a36Sopenharmony_ci#else 12162306a36Sopenharmony_ci#define is_generic_config(x) 0 12262306a36Sopenharmony_ci#endif 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr, int type) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct hda_codec *codec; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "ehdaudio%dD%d", bus->idx, addr); 12962306a36Sopenharmony_ci if (IS_ERR(codec)) { 13062306a36Sopenharmony_ci dev_err(bus->dev, "device init failed for hdac device\n"); 13162306a36Sopenharmony_ci return codec; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci codec->core.type = type; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return codec; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* probe individual codec */ 14062306a36Sopenharmony_cistatic int hda_codec_probe(struct snd_sof_dev *sdev, int address) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct hdac_hda_priv *hda_priv; 14362306a36Sopenharmony_ci struct hda_bus *hbus = sof_to_hbus(sdev); 14462306a36Sopenharmony_ci struct hda_codec *codec; 14562306a36Sopenharmony_ci u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | 14662306a36Sopenharmony_ci (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; 14762306a36Sopenharmony_ci u32 resp = -1; 14862306a36Sopenharmony_ci int ret, retry = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci do { 15162306a36Sopenharmony_ci mutex_lock(&hbus->core.cmd_mutex); 15262306a36Sopenharmony_ci snd_hdac_bus_send_cmd(&hbus->core, hda_cmd); 15362306a36Sopenharmony_ci snd_hdac_bus_get_response(&hbus->core, address, &resp); 15462306a36Sopenharmony_ci mutex_unlock(&hbus->core.cmd_mutex); 15562306a36Sopenharmony_ci } while (resp == -1 && retry++ < CODEC_PROBE_RETRIES); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (resp == -1) 15862306a36Sopenharmony_ci return -EIO; 15962306a36Sopenharmony_ci dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n", 16062306a36Sopenharmony_ci address, resp); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL); 16362306a36Sopenharmony_ci if (!hda_priv) 16462306a36Sopenharmony_ci return -ENOMEM; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci codec = hda_codec_device_init(&hbus->core, address, HDA_DEV_LEGACY); 16762306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(codec); 16862306a36Sopenharmony_ci if (ret < 0) 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci hda_priv->codec = codec; 17262306a36Sopenharmony_ci dev_set_drvdata(&codec->core.dev, hda_priv); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) { 17562306a36Sopenharmony_ci if (!hbus->core.audio_component) { 17662306a36Sopenharmony_ci dev_dbg(sdev->dev, 17762306a36Sopenharmony_ci "iDisp hw present but no driver\n"); 17862306a36Sopenharmony_ci ret = -ENOENT; 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci hda_priv->need_display_power = true; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (is_generic_config(hbus)) 18562306a36Sopenharmony_ci codec->probe_id = HDA_CODEC_ID_GENERIC; 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci codec->probe_id = 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci ret = hda_codec_load_module(codec); 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * handle ret==0 (no driver bound) as an error, but pass 19262306a36Sopenharmony_ci * other return codes without modification 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci if (ret == 0) 19562306a36Sopenharmony_ci ret = -ENOENT; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciout: 19862306a36Sopenharmony_ci if (ret < 0) { 19962306a36Sopenharmony_ci snd_hdac_device_unregister(&codec->core); 20062306a36Sopenharmony_ci put_device(&codec->core.dev); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* Codec initialization */ 20762306a36Sopenharmony_civoid hda_codec_probe_bus(struct snd_sof_dev *sdev) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 21062306a36Sopenharmony_ci int i, ret; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 21362306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 21462306a36Sopenharmony_ci return; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* probe codecs in avail slots */ 21762306a36Sopenharmony_ci for (i = 0; i < HDA_MAX_CODECS; i++) { 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!(bus->codec_mask & (1 << i))) 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = hda_codec_probe(sdev, i); 22362306a36Sopenharmony_ci if (ret < 0) { 22462306a36Sopenharmony_ci dev_warn(bus->dev, "codec #%d probe error, ret: %d\n", 22562306a36Sopenharmony_ci i, ret); 22662306a36Sopenharmony_ci bus->codec_mask &= ~BIT(i); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_probe_bus, SND_SOC_SOF_HDA_AUDIO_CODEC); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_civoid hda_codec_check_for_state_change(struct snd_sof_dev *sdev) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 23562306a36Sopenharmony_ci unsigned int codec_mask; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci codec_mask = snd_hdac_chip_readw(bus, STATESTS); 23862306a36Sopenharmony_ci if (codec_mask) { 23962306a36Sopenharmony_ci hda_codec_jack_check(sdev); 24062306a36Sopenharmony_ci snd_hdac_chip_writew(bus, STATESTS, codec_mask); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_check_for_state_change, SND_SOC_SOF_HDA_AUDIO_CODEC); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid hda_codec_detect_mask(struct snd_sof_dev *sdev) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 25062306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 25162306a36Sopenharmony_ci return; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Accept unsolicited responses */ 25462306a36Sopenharmony_ci snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* detect codecs */ 25762306a36Sopenharmony_ci if (!bus->codec_mask) { 25862306a36Sopenharmony_ci bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); 25962306a36Sopenharmony_ci dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (hda_codec_mask != -1) { 26362306a36Sopenharmony_ci bus->codec_mask &= hda_codec_mask; 26462306a36Sopenharmony_ci dev_dbg(bus->dev, "filtered codec_mask = 0x%lx\n", 26562306a36Sopenharmony_ci bus->codec_mask); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_detect_mask, SND_SOC_SOF_HDA_AUDIO_CODEC); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_civoid hda_codec_init_cmd_io(struct snd_sof_dev *sdev) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 27562306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* initialize the codec command I/O */ 27962306a36Sopenharmony_ci snd_hdac_bus_init_cmd_io(bus); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_init_cmd_io, SND_SOC_SOF_HDA_AUDIO_CODEC); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_civoid hda_codec_resume_cmd_io(struct snd_sof_dev *sdev) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 28862306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* set up CORB/RIRB buffers if was on before suspend */ 29262306a36Sopenharmony_ci if (bus->cmd_dma_state) 29362306a36Sopenharmony_ci snd_hdac_bus_init_cmd_io(bus); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_resume_cmd_io, SND_SOC_SOF_HDA_AUDIO_CODEC); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_civoid hda_codec_stop_cmd_io(struct snd_sof_dev *sdev) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 30262306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* initialize the codec command I/O */ 30662306a36Sopenharmony_ci snd_hdac_bus_stop_cmd_io(bus); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_stop_cmd_io, SND_SOC_SOF_HDA_AUDIO_CODEC); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_civoid hda_codec_suspend_cmd_io(struct snd_sof_dev *sdev) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 31562306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 31662306a36Sopenharmony_ci return; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* stop the CORB/RIRB DMA if it is On */ 31962306a36Sopenharmony_ci if (bus->cmd_dma_state) 32062306a36Sopenharmony_ci snd_hdac_bus_stop_cmd_io(bus); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_suspend_cmd_io, SND_SOC_SOF_HDA_AUDIO_CODEC); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_civoid hda_codec_rirb_status_clear(struct snd_sof_dev *sdev) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 33062306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* clear rirb status */ 33462306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_rirb_status_clear, SND_SOC_SOF_HDA_AUDIO_CODEC); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_civoid hda_codec_set_codec_wakeup(struct snd_sof_dev *sdev, bool status) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci snd_hdac_set_codec_wakeup(bus, status); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_set_codec_wakeup, SND_SOC_SOF_HDA_AUDIO_CODEC); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cibool hda_codec_check_rirb_status(struct snd_sof_dev *sdev) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 35262306a36Sopenharmony_ci bool active = false; 35362306a36Sopenharmony_ci u32 rirb_status; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 35662306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 35762306a36Sopenharmony_ci return false; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci rirb_status = snd_hdac_chip_readb(bus, RIRBSTS); 36062306a36Sopenharmony_ci if (rirb_status & RIRB_INT_MASK) { 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * Clearing the interrupt status here ensures 36362306a36Sopenharmony_ci * that no interrupt gets masked after the RIRB 36462306a36Sopenharmony_ci * wp is read in snd_hdac_bus_update_rirb. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBSTS, 36762306a36Sopenharmony_ci RIRB_INT_MASK); 36862306a36Sopenharmony_ci active = true; 36962306a36Sopenharmony_ci if (rirb_status & RIRB_INT_RESPONSE) 37062306a36Sopenharmony_ci snd_hdac_bus_update_rirb(bus); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci return active; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_check_rirb_status, SND_SOC_SOF_HDA_AUDIO_CODEC); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_civoid hda_codec_device_remove(struct snd_sof_dev *sdev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 38162306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 38262306a36Sopenharmony_ci return; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* codec removal, invoke bus_device_remove */ 38562306a36Sopenharmony_ci snd_hdac_ext_bus_device_remove(bus); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_device_remove, SND_SOC_SOF_HDA_AUDIO_CODEC); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) && IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_civoid hda_codec_i915_display_power(struct snd_sof_dev *sdev, bool enable) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 39862306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 39962306a36Sopenharmony_ci return; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (HDA_IDISP_CODEC(bus->codec_mask)) { 40262306a36Sopenharmony_ci dev_dbg(bus->dev, "Turning i915 HDAC power %d\n", enable); 40362306a36Sopenharmony_ci snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, enable); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_i915_display_power, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint hda_codec_i915_init(struct snd_sof_dev *sdev) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 41162306a36Sopenharmony_ci int ret; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 41462306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* i915 exposes a HDA codec for HDMI audio */ 41862306a36Sopenharmony_ci ret = snd_hdac_i915_init(bus); 41962306a36Sopenharmony_ci if (ret < 0) 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* codec_mask not yet known, power up for probe */ 42362306a36Sopenharmony_ci snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_i915_init, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciint hda_codec_i915_exit(struct snd_sof_dev *sdev) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct hdac_bus *bus = sof_to_bus(sdev); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && 43462306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!bus->audio_component) 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* power down unconditionally */ 44162306a36Sopenharmony_ci snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return snd_hdac_i915_exit(bus); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(hda_codec_i915_exit, SND_SOC_SOF_HDA_AUDIO_CODEC_I915); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci#endif 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 450