Commit b5df2a7d authored by Cezary Rojewski's avatar Cezary Rojewski Committed by Mark Brown

ASoC: codecs: Add HD-Audio codec driver

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 time, 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.

Relation with hdac_hda:

Addition of parallel solution is motivated by behavioral differences
between hdac_hda.c and its legacy equivalent found in sound/pci/hda
e.g.: lack of dynamic, based on codec capabilities, resource allocation
and high cost of removing such differences on actively used targets.
Major goal of codec driver presented here is to follow HD-Audio legacy
behavior in 1:1 fashion by becoming a wrapper. Doing so increases code
coverage of the legacy code and reduces the maintenance cost for both
solutions.
Signed-off-by: default avatarCezary Rojewski <cezary.rojewski@intel.com>
Link: https://lore.kernel.org/r/20220511162403.3987658-3-cezary.rojewski@intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 90b12a88
...@@ -937,6 +937,16 @@ config SND_SOC_HDAC_HDA ...@@ -937,6 +937,16 @@ config SND_SOC_HDAC_HDA
tristate tristate
select SND_HDA select SND_HDA
config SND_SOC_HDA
tristate "HD-Audio codec driver"
select SND_HDA_EXT_CORE
select SND_HDA
help
This enables HD-Audio codec support in ASoC subsystem. Compared
to SND_SOC_HDAC_HDA, driver's behavior is identical to HD-Audio
legacy solution - including the dynamic resource allocation
based on actual codec capabilities.
config SND_SOC_ICS43432 config SND_SOC_ICS43432
tristate "ICS43423 and compatible i2s microphones" tristate "ICS43423 and compatible i2s microphones"
......
...@@ -106,6 +106,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o ...@@ -106,6 +106,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
snd-soc-gtm601-objs := gtm601.o snd-soc-gtm601-objs := gtm601.o
snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o
snd-soc-hdac-hda-objs := hdac_hda.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-ics43432-objs := ics43432.o
snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-inno-rk3036-objs := inno_rk3036.o
snd-soc-isabelle-objs := isabelle.o snd-soc-isabelle-objs := isabelle.o
...@@ -458,6 +459,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.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_GTM601) += snd-soc-gtm601.o
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.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_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_ICS43432) += snd-soc-ics43432.o
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
......
// SPDX-License-Identifier: GPL-2.0
//
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
//
// Author: Cezary Rojewski <cezary.rojewski@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);
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
*
* Author: Cezary Rojewski <cezary.rojewski@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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment