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