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