Skip to content

Commit b5df2a7

Browse files
crojewsk-intelbroonie
authored andcommitted
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: Cezary Rojewski <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 90b12a8 commit b5df2a7

File tree

5 files changed

+528
-0
lines changed

5 files changed

+528
-0
lines changed

sound/soc/codecs/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,16 @@ config SND_SOC_HDAC_HDA
937937
tristate
938938
select SND_HDA
939939

940+
config SND_SOC_HDA
941+
tristate "HD-Audio codec driver"
942+
select SND_HDA_EXT_CORE
943+
select SND_HDA
944+
help
945+
This enables HD-Audio codec support in ASoC subsystem. Compared
946+
to SND_SOC_HDAC_HDA, driver's behavior is identical to HD-Audio
947+
legacy solution - including the dynamic resource allocation
948+
based on actual codec capabilities.
949+
940950
config SND_SOC_ICS43432
941951
tristate "ICS43423 and compatible i2s microphones"
942952

sound/soc/codecs/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
106106
snd-soc-gtm601-objs := gtm601.o
107107
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
108108
snd-soc-hdac-hda-objs := hdac_hda.o
109+
snd-soc-hda-codec-objs := hda.o hda-dai.o
109110
snd-soc-ics43432-objs := ics43432.o
110111
snd-soc-inno-rk3036-objs := inno_rk3036.o
111112
snd-soc-isabelle-objs := isabelle.o
@@ -458,6 +459,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
458459
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
459460
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
460461
obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
462+
obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o
461463
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
462464
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
463465
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o

sound/soc/codecs/hda-dai.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
//
3+
// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4+
//
5+
// Author: Cezary Rojewski <[email protected]>
6+
//
7+
8+
#include <sound/soc.h>
9+
#include <sound/hda_codec.h>
10+
#include "hda.h"
11+
12+
static int hda_codec_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
13+
{
14+
struct hda_pcm_stream *stream_info;
15+
struct hda_codec *codec;
16+
struct hda_pcm *pcm;
17+
int ret;
18+
19+
codec = dev_to_hda_codec(dai->dev);
20+
stream_info = snd_soc_dai_get_dma_data(dai, substream);
21+
pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
22+
23+
dev_dbg(dai->dev, "open stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
24+
codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
25+
26+
snd_hda_codec_pcm_get(pcm);
27+
28+
ret = stream_info->ops.open(stream_info, codec, substream);
29+
if (ret < 0) {
30+
dev_err(dai->dev, "codec open failed: %d\n", ret);
31+
snd_hda_codec_pcm_put(pcm);
32+
return ret;
33+
}
34+
35+
return 0;
36+
}
37+
38+
static void hda_codec_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
39+
{
40+
struct hda_pcm_stream *stream_info;
41+
struct hda_codec *codec;
42+
struct hda_pcm *pcm;
43+
int ret;
44+
45+
codec = dev_to_hda_codec(dai->dev);
46+
stream_info = snd_soc_dai_get_dma_data(dai, substream);
47+
pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]);
48+
49+
dev_dbg(dai->dev, "close stream codec: %08x, info: %p, pcm: %p %s substream: %p\n",
50+
codec->core.vendor_id, stream_info, pcm, pcm->name, substream);
51+
52+
ret = stream_info->ops.close(stream_info, codec, substream);
53+
if (ret < 0)
54+
dev_err(dai->dev, "codec close failed: %d\n", ret);
55+
56+
snd_hda_codec_pcm_put(pcm);
57+
}
58+
59+
static int hda_codec_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
60+
{
61+
struct hda_pcm_stream *stream_info;
62+
struct hda_codec *codec;
63+
64+
codec = dev_to_hda_codec(dai->dev);
65+
stream_info = snd_soc_dai_get_dma_data(dai, substream);
66+
67+
snd_hda_codec_cleanup(codec, stream_info, substream);
68+
69+
return 0;
70+
}
71+
72+
static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
73+
{
74+
struct snd_pcm_runtime *runtime = substream->runtime;
75+
struct hda_pcm_stream *stream_info;
76+
struct hdac_stream *stream;
77+
struct hda_codec *codec;
78+
unsigned int format;
79+
int ret;
80+
81+
codec = dev_to_hda_codec(dai->dev);
82+
stream = substream->runtime->private_data;
83+
stream_info = snd_soc_dai_get_dma_data(dai, substream);
84+
format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format,
85+
runtime->sample_bits, 0);
86+
87+
ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream);
88+
if (ret < 0) {
89+
dev_err(dai->dev, "codec prepare failed: %d\n", ret);
90+
return ret;
91+
}
92+
93+
return 0;
94+
}
95+
96+
const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops = {
97+
.startup = hda_codec_dai_startup,
98+
.shutdown = hda_codec_dai_shutdown,
99+
.hw_free = hda_codec_dai_hw_free,
100+
.prepare = hda_codec_dai_prepare,
101+
};
102+
EXPORT_SYMBOL_GPL(snd_soc_hda_codec_dai_ops);

0 commit comments

Comments
 (0)