[PATCH 02/14] ASoC: codecs: Add HD-Audio codec driver

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Wed Apr 27 17:47:12 CEST 2022



On 4/27/22 03:18, Cezary Rojewski wrote:
> Add generic ASoC equivalent of ALSA HD-Audio codec. This codec is
> designed to follow HDA_DEV_LEGACY convention. Driver wrapps existing
> hda_codec.c handlers to prevent code duplication within the newly added
> code. Number of DAIs created is dependent on capabilities exposed by the
> codec itself. Because of this, single solution can be applied to support
> every single HD-Audio codec type.
> 
> At the same, through the ASoC topology, platform drivers may limit the
> number of endpoints available to the userspace as codec driver exposes
> BE DAIs only.
> 
> Both hda_codec_probe() and hda_codec_remove() declare their expectations
> on device's usage_count and suspended-status. This is to catch any
> unexpected behavior as PM-related code for HD-Audio has been changing
> quite a bit throughout the years.
> 
> In order for codec DAI list to reflect its actual PCM capabilities, PCMs
> need to be built and that can only happen once codec device is
> constructed. To do that, a valid component->card->snd_card pointer is
> needed. Said pointer will be provided by the framework once all card
> components are accounted for and their probing can begin. Usage of
> "binder" BE DAI solves the problem - codec can be listed as one of
> HD-Audio card components without declaring any actual BE DAIs
> statically.

I am surprised the explanations don't even mention the existence of hdac_hda.c

/*

 * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers

 * with ASoC platform drivers. These APIs are called by the legacy HDA

 * codec drivers using hdac_ext_bus_ops ops.

 */


I thought the series was about adding machine drivers, but this also adds code on the sound/soc/codecs/ side which I didn't see coming.

I am not qualified to review this part of the code, I just wonder about duplication of functionality.

At the very least an explanation on why you decided to NOT use hdac_hda.c would be useful to reviewers and maintainers.

Thanks.


> 
> Signed-off-by: Cezary Rojewski <cezary.rojewski at intel.com>
> ---
>  sound/soc/codecs/Kconfig   |   5 +
>  sound/soc/codecs/Makefile  |   2 +
>  sound/soc/codecs/hda-dai.c | 102 ++++++++++
>  sound/soc/codecs/hda.c     | 395 +++++++++++++++++++++++++++++++++++++
>  sound/soc/codecs/hda.h     |  19 ++
>  5 files changed, 523 insertions(+)
>  create mode 100644 sound/soc/codecs/hda-dai.c
>  create mode 100644 sound/soc/codecs/hda.c
>  create mode 100644 sound/soc/codecs/hda.h
> 
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index b106e5517090..23fdbf97e453 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -937,6 +937,11 @@ config SND_SOC_HDAC_HDA
>  	tristate
>  	select SND_HDA
>  
> +config SND_SOC_HDA
> +	tristate "HD-Audio codec driver"
> +	select SND_HDA_EXT_CORE
> +	select SND_HDA
> +
>  config SND_SOC_ICS43432
>  	tristate "ICS43423 and compatible i2s microphones"
>  
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 28dc4edfd01f..d32026ae326f 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -106,6 +106,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
>  snd-soc-gtm601-objs := gtm601.o
>  snd-soc-hdac-hdmi-objs := hdac_hdmi.o
>  snd-soc-hdac-hda-objs := hdac_hda.o
> +snd-soc-hda-codec-objs := hda.o hda-dai.o
>  snd-soc-ics43432-objs := ics43432.o
>  snd-soc-inno-rk3036-objs := inno_rk3036.o
>  snd-soc-isabelle-objs := isabelle.o
> @@ -458,6 +459,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
>  obj-$(CONFIG_SND_SOC_GTM601)    += snd-soc-gtm601.o
>  obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
>  obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
> +obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o
>  obj-$(CONFIG_SND_SOC_ICS43432)	+= snd-soc-ics43432.o
>  obj-$(CONFIG_SND_SOC_INNO_RK3036)	+= snd-soc-inno-rk3036.o
>  obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
> diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c
> new file mode 100644
> index 000000000000..5371ff086261
> --- /dev/null
> +++ b/sound/soc/codecs/hda-dai.c
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
> +//
> +// Author: Cezary Rojewski <cezary.rojewski at intel.com>
> +//
> +
> +#include <sound/soc.h>
> +#include <sound/hda_codec.h>
> +#include "hda.h"
> +
> +static int hda_codec_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct hda_pcm_stream *stream_info;
> +	struct hda_codec *codec;
> +	struct hda_pcm *pcm;
> +	int ret;
> +
> +	codec = dev_to_hda_codec(dai->dev);
> +	stream_info = snd_soc_dai_get_dma_data(dai, substream);
> +	pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
> +
> +	dev_dbg(dai->dev, "open stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
> +		codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
> +
> +	snd_hda_codec_pcm_get(pcm);
> +
> +	ret = stream_info->ops.open(stream_info, codec, substream);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "codec open failed: %d\n", ret);
> +		snd_hda_codec_pcm_put(pcm);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void hda_codec_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct hda_pcm_stream *stream_info;
> +	struct hda_codec *codec;
> +	struct hda_pcm *pcm;
> +	int ret;
> +
> +	codec = dev_to_hda_codec(dai->dev);
> +	stream_info = snd_soc_dai_get_dma_data(dai, substream);
> +	pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
> +
> +	dev_dbg(dai->dev, "close stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
> +		codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
> +
> +	ret = stream_info->ops.close(stream_info, codec, substream);
> +	if (ret < 0)
> +		dev_err(dai->dev, "codec close failed: %d\n", ret);
> +
> +	snd_hda_codec_pcm_put(pcm);
> +}
> +
> +static int hda_codec_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct hda_pcm_stream *stream_info;
> +	struct hda_codec *codec;
> +
> +	codec = dev_to_hda_codec(dai->dev);
> +	stream_info = snd_soc_dai_get_dma_data(dai, substream);
> +
> +	snd_hda_codec_cleanup(codec, stream_info, substream);
> +
> +	return 0;
> +}
> +
> +static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct hda_pcm_stream *stream_info;
> +	struct hdac_stream *stream;
> +	struct hda_codec *codec;
> +	unsigned int format;
> +	int ret;
> +
> +	codec = dev_to_hda_codec(dai->dev);
> +	stream = substream->runtime->private_data;
> +	stream_info = snd_soc_dai_get_dma_data(dai, substream);
> +	format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
> +					     runtime->sample_bits, 0);
> +
> +	ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "codec prepare failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops = {
> +	.startup = hda_codec_dai_startup,
> +	.shutdown = hda_codec_dai_shutdown,
> +	.hw_free = hda_codec_dai_hw_free,
> +	.prepare = hda_codec_dai_prepare,
> +};
> +EXPORT_SYMBOL_GPL(snd_soc_hda_codec_dai_ops);
> diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
> new file mode 100644
> index 000000000000..edcb8bc6806b
> --- /dev/null
> +++ b/sound/soc/codecs/hda.c
> @@ -0,0 +1,395 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
> +//
> +// Author: Cezary Rojewski <cezary.rojewski at intel.com>
> +//
> +
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <sound/soc.h>
> +#include <sound/hdaudio_ext.h>
> +#include <sound/hda_i915.h>
> +#include <sound/hda_codec.h>
> +#include "hda.h"
> +
> +static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count,
> +				 struct snd_soc_dai_driver **drivers)
> +{
> +	struct device *dev = &codec->core.dev;
> +	struct snd_soc_dai_driver *drvs;
> +	struct hda_pcm *pcm;
> +	int i;
> +
> +	drvs = devm_kcalloc(dev, pcm_count, sizeof(*drvs), GFP_KERNEL);
> +	if (!drvs)
> +		return -ENOMEM;
> +
> +	pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
> +
> +	for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
> +		struct snd_soc_pcm_stream *stream;
> +		int dir;
> +
> +		dev_info(dev, "creating for %s %d\n", pcm->name, i);
> +		drvs[i].id = i;
> +		drvs[i].name = pcm->name;
> +		drvs[i].ops = &snd_soc_hda_codec_dai_ops;
> +
> +		dir = SNDRV_PCM_STREAM_PLAYBACK;
> +		stream = &drvs[i].playback;
> +		if (!pcm->stream[dir].substreams) {
> +			dev_info(dev, "skipping playback dai for %s\n", pcm->name);
> +			goto capture_dais;
> +		}
> +
> +		stream->stream_name =
> +			devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
> +				       snd_pcm_direction_name(dir));
> +		if (!stream->stream_name)
> +			return -ENOMEM;
> +		stream->channels_min = pcm->stream[dir].channels_min;
> +		stream->channels_max = pcm->stream[dir].channels_max;
> +		stream->rates = pcm->stream[dir].rates;
> +		stream->formats = pcm->stream[dir].formats;
> +		stream->sig_bits = pcm->stream[dir].maxbps;
> +
> +capture_dais:
> +		dir = SNDRV_PCM_STREAM_CAPTURE;
> +		stream = &drvs[i].capture;
> +		if (!pcm->stream[dir].substreams) {
> +			dev_info(dev, "skipping capture dai for %s\n", pcm->name);
> +			continue;
> +		}
> +
> +		stream->stream_name =
> +			devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name,
> +				       snd_pcm_direction_name(dir));
> +		if (!stream->stream_name)
> +			return -ENOMEM;
> +		stream->channels_min = pcm->stream[dir].channels_min;
> +		stream->channels_max = pcm->stream[dir].channels_max;
> +		stream->rates = pcm->stream[dir].rates;
> +		stream->formats = pcm->stream[dir].formats;
> +		stream->sig_bits = pcm->stream[dir].maxbps;
> +	}
> +
> +	*drivers = drvs;
> +	return 0;
> +}
> +
> +static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_component *component)
> +{
> +	struct snd_soc_dai_driver *drvs = NULL;
> +	struct snd_soc_dapm_context *dapm;
> +	struct hda_pcm *pcm;
> +	int ret, pcm_count = 0;
> +
> +	if (list_empty(&codec->pcm_list_head))
> +		return -EINVAL;
> +	list_for_each_entry(pcm, &codec->pcm_list_head, list)
> +		pcm_count++;
> +
> +	ret = hda_codec_create_dais(codec, pcm_count, &drvs);
> +	if (ret < 0)
> +		return ret;
> +
> +	dapm = snd_soc_component_get_dapm(component);
> +
> +	list_for_each_entry(pcm, &codec->pcm_list_head, list) {
> +		struct snd_soc_dai *dai;
> +
> +		dai = snd_soc_register_dai(component, drvs, false);
> +		if (!dai) {
> +			dev_err(component->dev, "register dai for %s failed\n", pcm->name);
> +			return -EINVAL;
> +		}
> +
> +		ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
> +		if (ret < 0) {
> +			dev_err(component->dev, "create widgets failed: %d\n", ret);
> +			snd_soc_unregister_dai(dai);
> +			return ret;
> +		}
> +
> +		snd_soc_dai_init_dma_data(dai, &pcm->stream[0], &pcm->stream[1]);
> +		drvs++;
> +	}
> +
> +	return 0;
> +}
> +
> +static void hda_codec_unregister_dais(struct hda_codec *codec,
> +				      struct snd_soc_component *component)
> +{
> +	struct snd_soc_dai *dai, *save;
> +	struct hda_pcm *pcm;
> +
> +	for_each_component_dais_safe(component, dai, save) {
> +		list_for_each_entry(pcm, &codec->pcm_list_head, list) {
> +			if (strcmp(dai->driver->name, pcm->name))
> +				continue;
> +
> +			if (dai->playback_widget)
> +				snd_soc_dapm_free_widget(dai->playback_widget);
> +			if (dai->capture_widget)
> +				snd_soc_dapm_free_widget(dai->capture_widget);
> +			snd_soc_unregister_dai(dai);
> +			break;
> +		}
> +	}
> +}
> +
> +int hda_codec_probe_complete(struct hda_codec *codec)
> +{
> +	struct hdac_device *hdev = &codec->core;
> +	struct hdac_bus *bus = hdev->bus;
> +	int ret;
> +
> +	ret = snd_hda_codec_build_controls(codec);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "unable to create controls %d\n", ret);
> +		goto out;
> +	}
> +
> +	/* Bus suspended codecs as it does not manage their pm */
> +	pm_runtime_set_active(&hdev->dev);
> +	/* rpm was forbidden in snd_hda_codec_device_new() */
> +	snd_hda_codec_set_power_save(codec, 2000);
> +	snd_hda_codec_register(codec);
> +out:
> +	/* Complement pm_runtime_get_sync(bus) in probe */
> +	pm_runtime_mark_last_busy(bus->dev);
> +	pm_runtime_put_autosuspend(bus->dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(hda_codec_probe_complete);
> +
> +/* Expects codec with usage_count=1 and status=suspended */
> +static int hda_codec_probe(struct snd_soc_component *component)
> +{
> +	struct hda_codec *codec = dev_to_hda_codec(component->dev);
> +	struct hdac_device *hdev = &codec->core;
> +	struct hdac_bus *bus = hdev->bus;
> +	struct hdac_ext_link *hlink;
> +	hda_codec_patch_t patch;
> +	int ret;
> +
> +#ifdef CONFIG_PM
> +	WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
> +		!pm_runtime_status_suspended(&hdev->dev));
> +#endif
> +
> +	hlink = snd_hdac_ext_bus_link_at(bus, hdev->addr);
> +	if (!hlink) {
> +		dev_err(&hdev->dev, "hdac link not found\n");
> +		return -EIO;
> +	}
> +
> +	pm_runtime_get_sync(bus->dev);
> +	if (hda_codec_is_display(codec))
> +		snd_hdac_display_power(bus, hdev->addr, true);
> +	snd_hdac_ext_bus_link_get(bus, hlink);
> +
> +	ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec,
> +				       false);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "create hda codec failed: %d\n", ret);
> +		goto device_new_err;
> +	}
> +
> +	ret = snd_hda_codec_set_name(codec, codec->preset->name);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "name failed %s\n", codec->preset->name);
> +		goto err;
> +	}
> +
> +	ret = snd_hdac_regmap_init(&codec->core);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "regmap init failed\n");
> +		goto err;
> +	}
> +
> +	patch = (hda_codec_patch_t)codec->preset->driver_data;
> +	if (!patch) {
> +		dev_err(&hdev->dev, "no patch specified?\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	ret = patch(codec);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "patch failed %d\n", ret);
> +		goto err;
> +	}
> +
> +	/* configure codec for 1:1 PCM:DAI mapping */
> +	codec->mst_no_extra_pcms = 1;
> +
> +	ret = snd_hda_codec_parse_pcms(codec);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret);
> +		goto parse_pcms_err;
> +	}
> +
> +	ret = hda_codec_register_dais(codec, component);
> +	if (ret < 0) {
> +		dev_err(&hdev->dev, "update dais failed: %d\n", ret);
> +		goto parse_pcms_err;
> +	}
> +
> +	if (!hda_codec_is_display(codec)) {
> +		ret = hda_codec_probe_complete(codec);
> +		if (ret < 0)
> +			goto complete_err;
> +	}
> +
> +	codec->core.lazy_cache = true;
> +
> +	return 0;
> +
> +complete_err:
> +	hda_codec_unregister_dais(codec, component);
> +parse_pcms_err:
> +	if (codec->patch_ops.free)
> +		codec->patch_ops.free(codec);
> +err:
> +	snd_hda_codec_cleanup_for_unbind(codec);
> +device_new_err:
> +	if (hda_codec_is_display(codec))
> +		snd_hdac_display_power(bus, hdev->addr, false);
> +
> +	snd_hdac_ext_bus_link_put(bus, hlink);
> +
> +	pm_runtime_mark_last_busy(bus->dev);
> +	pm_runtime_put_autosuspend(bus->dev);
> +	return ret;
> +}
> +
> +/* Leaves codec with usage_count=1 and status=suspended */
> +static void hda_codec_remove(struct snd_soc_component *component)
> +{
> +	struct hda_codec *codec = dev_to_hda_codec(component->dev);
> +	struct hdac_device *hdev = &codec->core;
> +	struct hdac_bus *bus = hdev->bus;
> +	struct hdac_ext_link *hlink;
> +	bool was_registered = codec->registered;
> +
> +	/* Don't allow any more runtime suspends */
> +	pm_runtime_forbid(&hdev->dev);
> +
> +	hda_codec_unregister_dais(codec, component);
> +
> +	if (codec->patch_ops.free)
> +		codec->patch_ops.free(codec);
> +
> +	snd_hda_codec_cleanup_for_unbind(codec);
> +	pm_runtime_put_noidle(&hdev->dev);
> +	/* snd_hdac_device_exit() is only called on bus remove */
> +	pm_runtime_set_suspended(&hdev->dev);
> +
> +	if (hda_codec_is_display(codec))
> +		snd_hdac_display_power(bus, hdev->addr, false);
> +
> +	hlink = snd_hdac_ext_bus_link_at(bus, hdev->addr);
> +	if (hlink)
> +		snd_hdac_ext_bus_link_put(bus, hlink);
> +	/*
> +	 * HDMI card's hda_codec_probe_complete() (see late_probe()) may
> +	 * not be called due to early error, leaving bus uc unbalanced
> +	 */
> +	if (!was_registered) {
> +		pm_runtime_mark_last_busy(bus->dev);
> +		pm_runtime_put_autosuspend(bus->dev);
> +	}
> +
> +#ifdef CONFIG_PM
> +	WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 ||
> +		!pm_runtime_status_suspended(&hdev->dev));
> +#endif
> +}
> +
> +static const struct snd_soc_dapm_route hda_dapm_routes[] = {
> +	{"AIF1TX", NULL, "Codec Input Pin1"},
> +	{"AIF2TX", NULL, "Codec Input Pin2"},
> +	{"AIF3TX", NULL, "Codec Input Pin3"},
> +
> +	{"Codec Output Pin1", NULL, "AIF1RX"},
> +	{"Codec Output Pin2", NULL, "AIF2RX"},
> +	{"Codec Output Pin3", NULL, "AIF3RX"},
> +};
> +
> +static const struct snd_soc_dapm_widget hda_dapm_widgets[] = {
> +	/* Audio Interface */
> +	SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, SND_SOC_NOPM, 0, 0),
> +	SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0),
> +
> +	/* Input Pins */
> +	SND_SOC_DAPM_INPUT("Codec Input Pin1"),
> +	SND_SOC_DAPM_INPUT("Codec Input Pin2"),
> +	SND_SOC_DAPM_INPUT("Codec Input Pin3"),
> +
> +	/* Output Pins */
> +	SND_SOC_DAPM_OUTPUT("Codec Output Pin1"),
> +	SND_SOC_DAPM_OUTPUT("Codec Output Pin2"),
> +	SND_SOC_DAPM_OUTPUT("Codec Output Pin3"),
> +};
> +
> +static struct snd_soc_dai_driver card_binder_dai = {
> +	.id = -1,
> +	.name = "codec-probing-DAI",
> +};
> +
> +static int hda_hdev_attach(struct hdac_device *hdev)
> +{
> +	struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
> +	struct snd_soc_component_driver *comp_drv;
> +
> +	comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL);
> +	if (!comp_drv)
> +		return -ENOMEM;
> +
> +	/*
> +	 * It's save to rely on dev_name() rather than a copy as component
> +	 * driver's lifetime is directly tied to hda codec one
> +	 */
> +	comp_drv->name = dev_name(&hdev->dev);
> +	comp_drv->probe = hda_codec_probe;
> +	comp_drv->remove = hda_codec_remove;
> +	comp_drv->idle_bias_on = false;
> +	if (!hda_codec_is_display(codec)) {
> +		comp_drv->dapm_widgets = hda_dapm_widgets;
> +		comp_drv->num_dapm_widgets = ARRAY_SIZE(hda_dapm_widgets);
> +		comp_drv->dapm_routes = hda_dapm_routes;
> +		comp_drv->num_dapm_routes = ARRAY_SIZE(hda_dapm_routes);
> +	}
> +
> +	return snd_soc_register_component(&hdev->dev, comp_drv, &card_binder_dai, 1);
> +}
> +
> +static int hda_hdev_detach(struct hdac_device *hdev)
> +{
> +	struct hda_codec *codec = dev_to_hda_codec(&hdev->dev);
> +
> +	if (codec->registered)
> +		cancel_delayed_work_sync(&codec->jackpoll_work);
> +
> +	snd_soc_unregister_component(&hdev->dev);
> +
> +	return 0;
> +}
> +
> +const struct hdac_ext_bus_ops soc_hda_ext_bus_ops = {
> +	.hdev_attach = hda_hdev_attach,
> +	.hdev_detach = hda_hdev_detach,
> +};
> +EXPORT_SYMBOL_GPL(soc_hda_ext_bus_ops);
> +
> +MODULE_DESCRIPTION("HD-Audio codec driver");
> +MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski at intel.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/codecs/hda.h b/sound/soc/codecs/hda.h
> new file mode 100644
> index 000000000000..78a2be4945b1
> --- /dev/null
> +++ b/sound/soc/codecs/hda.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
> + *
> + * Author: Cezary Rojewski <cezary.rojewski at intel.com>
> + */
> +
> +#ifndef SND_SOC_CODECS_HDA_H
> +#define SND_SOC_CODECS_HDA_H
> +
> +#define hda_codec_is_display(codec) \
> +	((((codec)->core.vendor_id >> 16) & 0xFFFF) == 0x8086)
> +
> +extern const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops;
> +
> +extern const struct hdac_ext_bus_ops soc_hda_ext_bus_ops;
> +int hda_codec_probe_complete(struct hda_codec *codec);
> +
> +#endif


More information about the Alsa-devel mailing list