summaryrefslogtreecommitdiff
path: root/sound/soc/intel/avs/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/intel/avs/pcm.c')
-rw-r--r--sound/soc/intel/avs/pcm.c180
1 files changed, 142 insertions, 38 deletions
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index 945f9c0a6a54..5a2330e4e422 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -31,7 +31,12 @@ struct avs_dma_data {
struct hdac_ext_stream *host_stream;
};
+ struct snd_pcm_hw_constraint_list rate_list;
+ struct snd_pcm_hw_constraint_list channels_list;
+ struct snd_pcm_hw_constraint_list sample_bits_list;
+
struct work_struct period_elapsed_work;
+ struct hdac_ext_link *link;
struct snd_pcm_substream *substream;
};
@@ -74,6 +79,42 @@ void avs_period_elapsed(struct snd_pcm_substream *substream)
schedule_work(&data->period_elapsed_work);
}
+static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule);
+static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hw_constraint_list *r, *c, *s;
+ struct avs_dma_data *data;
+ int ret;
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ r = &(data->rate_list);
+ c = &(data->channels_list);
+ s = &(data->sample_bits_list);
+
+ ret = avs_path_set_constraint(data->adev, data->template, r, c, s);
+ if (ret <= 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, r);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, c);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -101,7 +142,7 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d
if (rtd->dai_link->ignore_suspend)
adev->num_lp_paths++;
- return 0;
+ return avs_hw_constraints_init(substream, dai);
}
static void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
@@ -114,6 +155,10 @@ static void avs_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc
if (rtd->dai_link->ignore_suspend)
data->adev->num_lp_paths--;
+ kfree(data->rate_list.list);
+ kfree(data->channels_list.list);
+ kfree(data->sample_bits_list.list);
+
snd_soc_dai_set_dma_data(dai, substream, NULL);
kfree(data);
}
@@ -161,6 +206,7 @@ static int avs_dai_be_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dpcm *dpcm;
be = snd_soc_substream_to_rtd(substream);
+ /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */
for_each_dpcm_fe(be, substream->stream, dpcm) {
fe = dpcm->fe;
fe_hw_params = &fe->dpcm[substream->stream].hw_params;
@@ -277,32 +323,75 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
.trigger = avs_dai_nonhda_be_trigger,
};
-static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+static int __avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
+ struct hdac_ext_link *link)
{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
struct avs_dma_data *data;
- struct hda_codec *codec;
int ret;
ret = avs_dai_startup(substream, dai);
if (ret)
return ret;
- codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
- link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ link_stream = snd_hdac_ext_stream_assign(&data->adev->base.core, substream,
HDAC_EXT_STREAM_TYPE_LINK);
if (!link_stream) {
avs_dai_shutdown(substream, dai);
return -EBUSY;
}
- data = snd_soc_dai_get_dma_data(dai, substream);
data->link_stream = link_stream;
- substream->runtime->private_data = link_stream;
+ data->link = link;
return 0;
}
+static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct hdac_ext_link *link;
+ struct avs_dma_data *data;
+ struct hda_codec *codec;
+ int ret;
+
+ codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
+
+ link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr);
+ if (!link)
+ return -EINVAL;
+
+ ret = __avs_dai_hda_be_startup(substream, dai, link);
+ if (!ret) {
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ substream->runtime->private_data = data->link_stream;
+ }
+
+ return ret;
+}
+
+static int avs_dai_i2shda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct hdac_ext_link *link;
+
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (!link)
+ return -EINVAL;
+ return __avs_dai_hda_be_startup(substream, dai, link);
+}
+
+static int avs_dai_dmichda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct hdac_ext_link *link;
+
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+ if (!link)
+ return -EINVAL;
+ return __avs_dai_hda_be_startup(substream, dai, link);
+}
+
static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
@@ -312,6 +401,14 @@ static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct
avs_dai_shutdown(substream, dai);
}
+static void avs_dai_althda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_hdac_ext_stream_release(data->link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ avs_dai_shutdown(substream, dai);
+}
+
static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai)
{
@@ -327,13 +424,8 @@ static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct avs_dma_data *data;
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
- struct hdac_ext_link *link;
- struct hda_codec *codec;
-
- dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ struct avs_dma_data *data;
data = snd_soc_dai_get_dma_data(dai, substream);
if (!data->path)
@@ -345,54 +437,43 @@ static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct sn
data->path = NULL;
/* clear link <-> stream mapping */
- codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
- link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr);
- if (!link)
- return -EINVAL;
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_hdac_ext_bus_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag);
+ snd_hdac_ext_bus_link_clear_stream_id(data->link,
+ hdac_stream(link_stream)->stream_tag);
return 0;
}
static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream);
const struct snd_soc_pcm_stream *stream_info;
struct hdac_ext_stream *link_stream;
- struct hdac_ext_link *link;
+ const struct snd_pcm_hw_params *p;
struct avs_dma_data *data;
- struct hda_codec *codec;
- struct hdac_bus *bus;
unsigned int format_val;
unsigned int bits;
int ret;
data = snd_soc_dai_get_dma_data(dai, substream);
link_stream = data->link_stream;
+ p = &be->dpcm[substream->stream].hw_params;
if (link_stream->link_prepared)
return 0;
- codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
- bus = &codec->bus->core;
stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
- bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ bits = snd_hdac_stream_format_bits(params_format(p), params_subformat(p),
stream_info->sig_bits);
- format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
+ format_val = snd_hdac_stream_format(params_channels(p), bits, params_rate(p));
- snd_hdac_ext_stream_decouple(bus, link_stream, true);
+ snd_hdac_ext_stream_decouple(&data->adev->base.core, link_stream, true);
snd_hdac_ext_stream_reset(link_stream);
snd_hdac_ext_stream_setup(link_stream, format_val);
- link = snd_hdac_ext_bus_get_hlink_by_addr(bus, codec->core.addr);
- if (!link)
- return -EINVAL;
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_hdac_ext_bus_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag);
+ snd_hdac_ext_bus_link_set_stream_id(data->link,
+ hdac_stream(link_stream)->stream_tag);
ret = avs_dai_prepare(substream, dai);
if (ret)
@@ -467,6 +548,26 @@ static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
.trigger = avs_dai_hda_be_trigger,
};
+__maybe_unused
+static const struct snd_soc_dai_ops avs_dai_i2shda_be_ops = {
+ .startup = avs_dai_i2shda_be_startup,
+ .shutdown = avs_dai_althda_be_shutdown,
+ .hw_params = avs_dai_hda_be_hw_params,
+ .hw_free = avs_dai_hda_be_hw_free,
+ .prepare = avs_dai_hda_be_prepare,
+ .trigger = avs_dai_hda_be_trigger,
+};
+
+__maybe_unused
+static const struct snd_soc_dai_ops avs_dai_dmichda_be_ops = {
+ .startup = avs_dai_dmichda_be_startup,
+ .shutdown = avs_dai_althda_be_shutdown,
+ .hw_params = avs_dai_hda_be_hw_params,
+ .hw_free = avs_dai_hda_be_hw_free,
+ .prepare = avs_dai_hda_be_prepare,
+ .trigger = avs_dai_hda_be_trigger,
+};
+
static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
struct snd_interval *interval = hw_param_interval(params, rule->var);
@@ -576,6 +677,7 @@ static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream,
hdac_stream(host_stream)->format_val = 0;
fe = snd_soc_substream_to_rtd(substream);
+ /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */
for_each_dpcm_be(fe, substream->stream, dpcm) {
be = dpcm->be;
be_hw_params = &be->dpcm[substream->stream].hw_params;
@@ -925,7 +1027,8 @@ static int avs_component_probe(struct snd_soc_component *component)
else
mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL,
"hda-generic-tplg.bin");
-
+ if (!mach->tplg_filename)
+ return -ENOMEM;
filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
mach->tplg_filename);
if (!filename)
@@ -1378,7 +1481,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
for_each_set_bit(i, &port_mask, ssp_count)
cpu_count += hweight_long(tdms[i]);
- cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL);
+ cpus = devm_kcalloc(adev->dev, cpu_count, sizeof(*cpus), GFP_KERNEL);
if (!cpus)
return -ENOMEM;
@@ -1564,6 +1667,7 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
if (ret < 0) {
dev_err(component->dev, "create widgets failed: %d\n",
ret);
+ snd_soc_unregister_dai(dai);
goto exit;
}
}
@@ -1578,8 +1682,8 @@ exit:
static void avs_component_hda_remove(struct snd_soc_component *component)
{
- avs_component_hda_unregister_dais(component);
avs_component_remove(component);
+ avs_component_hda_unregister_dais(component);
}
static int avs_component_hda_open(struct snd_soc_component *component,