|
@@ -133,16 +133,25 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
|
|
|
*/
|
|
|
bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
|
|
|
{
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
int i;
|
|
|
bool ignore = true;
|
|
|
|
|
|
if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
|
|
|
return true;
|
|
|
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ ignore &= !component->driver->pmdown_time;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* this will be removed */
|
|
|
for (i = 0; i < rtd->num_codecs; i++)
|
|
|
ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time;
|
|
|
|
|
|
- return rtd->cpu_dai->component->ignore_pmdown_time && ignore;
|
|
|
+ return ignore;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -459,7 +468,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
const char *codec_dai_name = "multicodec";
|
|
|
- int i, ret = 0;
|
|
|
+ int i, ret = 0, __ret;
|
|
|
|
|
|
pinctrl_pm_select_default_state(cpu_dai->dev);
|
|
|
for (i = 0; i < rtd->num_codecs; i++)
|
|
@@ -474,7 +483,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
|
|
|
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
|
|
|
|
|
|
/* startup the audio subsystem */
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
|
|
|
+ if (cpu_dai->driver->ops->startup) {
|
|
|
ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
|
|
|
if (ret < 0) {
|
|
|
dev_err(cpu_dai->dev, "ASoC: can't open interface"
|
|
@@ -483,7 +492,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->open) {
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->open) {
|
|
|
ret = platform->driver->ops->open(substream);
|
|
|
if (ret < 0) {
|
|
|
dev_err(platform->dev, "ASoC: can't open platform"
|
|
@@ -492,9 +501,32 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ ret = 0;
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->open)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ __ret = component->driver->ops->open(substream);
|
|
|
+ if (__ret < 0) {
|
|
|
+ dev_err(component->dev,
|
|
|
+ "ASoC: can't open component %s: %d\n",
|
|
|
+ component->name, ret);
|
|
|
+ ret = __ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (ret < 0)
|
|
|
+ goto component_err;
|
|
|
+
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
|
|
|
+ if (codec_dai->driver->ops->startup) {
|
|
|
ret = codec_dai->driver->ops->startup(substream,
|
|
|
codec_dai);
|
|
|
if (ret < 0) {
|
|
@@ -511,7 +543,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
|
|
|
codec_dai->rx_mask = 0;
|
|
|
}
|
|
|
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
|
|
|
+ if (rtd->dai_link->ops->startup) {
|
|
|
ret = rtd->dai_link->ops->startup(substream);
|
|
|
if (ret < 0) {
|
|
|
pr_err("ASoC: %s startup failed: %d\n",
|
|
@@ -585,7 +617,7 @@ dynamic:
|
|
|
return 0;
|
|
|
|
|
|
config_err:
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
|
|
|
+ if (rtd->dai_link->ops->shutdown)
|
|
|
rtd->dai_link->ops->shutdown(substream);
|
|
|
|
|
|
machine_err:
|
|
@@ -598,7 +630,22 @@ codec_dai_err:
|
|
|
codec_dai->driver->ops->shutdown(substream, codec_dai);
|
|
|
}
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->close)
|
|
|
+component_err:
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->close)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ component->driver->ops->close(substream);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->close)
|
|
|
platform->driver->ops->close(substream);
|
|
|
|
|
|
platform_err:
|
|
@@ -692,12 +739,26 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
|
|
|
codec_dai->driver->ops->shutdown(substream, codec_dai);
|
|
|
}
|
|
|
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
|
|
|
+ if (rtd->dai_link->ops->shutdown)
|
|
|
rtd->dai_link->ops->shutdown(substream);
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->close)
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->close)
|
|
|
platform->driver->ops->close(substream);
|
|
|
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->close)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ component->driver->ops->close(substream);
|
|
|
+ }
|
|
|
+
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
|
|
|
/* powered down playback stream now */
|
|
@@ -745,13 +806,15 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
int i, ret = 0;
|
|
|
|
|
|
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
|
|
|
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
|
|
|
+ if (rtd->dai_link->ops->prepare) {
|
|
|
ret = rtd->dai_link->ops->prepare(substream);
|
|
|
if (ret < 0) {
|
|
|
dev_err(rtd->card->dev, "ASoC: machine prepare error:"
|
|
@@ -760,7 +823,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->prepare) {
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->prepare) {
|
|
|
ret = platform->driver->ops->prepare(substream);
|
|
|
if (ret < 0) {
|
|
|
dev_err(platform->dev, "ASoC: platform prepare error:"
|
|
@@ -769,9 +832,28 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->prepare)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = component->driver->ops->prepare(substream);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(component->dev,
|
|
|
+ "ASoC: platform prepare error: %d\n", ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) {
|
|
|
+ if (codec_dai->driver->ops->prepare) {
|
|
|
ret = codec_dai->driver->ops->prepare(substream,
|
|
|
codec_dai);
|
|
|
if (ret < 0) {
|
|
@@ -783,7 +865,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) {
|
|
|
+ if (cpu_dai->driver->ops->prepare) {
|
|
|
ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
|
|
|
if (ret < 0) {
|
|
|
dev_err(cpu_dai->dev,
|
|
@@ -829,7 +911,7 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream,
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
- if (dai->driver->ops && dai->driver->ops->hw_params) {
|
|
|
+ if (dai->driver->ops->hw_params) {
|
|
|
ret = dai->driver->ops->hw_params(substream, params, dai);
|
|
|
if (ret < 0) {
|
|
|
dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n",
|
|
@@ -851,16 +933,13 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
- int i, ret = 0;
|
|
|
+ int i, ret = 0, __ret;
|
|
|
|
|
|
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
|
|
|
-
|
|
|
- ret = soc_pcm_params_symmetry(substream, params);
|
|
|
- if (ret)
|
|
|
- goto out;
|
|
|
-
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
|
|
|
+ if (rtd->dai_link->ops->hw_params) {
|
|
|
ret = rtd->dai_link->ops->hw_params(substream, params);
|
|
|
if (ret < 0) {
|
|
|
dev_err(rtd->card->dev, "ASoC: machine hw_params"
|
|
@@ -915,7 +994,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
|
if (ret < 0)
|
|
|
goto interface_err;
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->hw_params) {
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->hw_params) {
|
|
|
ret = platform->driver->ops->hw_params(substream, params);
|
|
|
if (ret < 0) {
|
|
|
dev_err(platform->dev, "ASoC: %s hw params failed: %d\n",
|
|
@@ -924,18 +1003,62 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ ret = 0;
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->hw_params)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ __ret = component->driver->ops->hw_params(substream, params);
|
|
|
+ if (__ret < 0) {
|
|
|
+ dev_err(component->dev,
|
|
|
+ "ASoC: %s hw params failed: %d\n",
|
|
|
+ component->name, ret);
|
|
|
+ ret = __ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (ret < 0)
|
|
|
+ goto component_err;
|
|
|
+
|
|
|
/* store the parameters for each DAIs */
|
|
|
cpu_dai->rate = params_rate(params);
|
|
|
cpu_dai->channels = params_channels(params);
|
|
|
cpu_dai->sample_bits =
|
|
|
snd_pcm_format_physical_width(params_format(params));
|
|
|
|
|
|
+ ret = soc_pcm_params_symmetry(substream, params);
|
|
|
+ if (ret)
|
|
|
+ goto component_err;
|
|
|
out:
|
|
|
mutex_unlock(&rtd->pcm_mutex);
|
|
|
return ret;
|
|
|
|
|
|
+component_err:
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->hw_free)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ component->driver->ops->hw_free(substream);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->hw_free)
|
|
|
+ platform->driver->ops->hw_free(substream);
|
|
|
+
|
|
|
platform_err:
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
|
|
|
+ if (cpu_dai->driver->ops->hw_free)
|
|
|
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
|
|
|
|
|
|
interface_err:
|
|
@@ -944,12 +1067,12 @@ interface_err:
|
|
|
codec_err:
|
|
|
while (--i >= 0) {
|
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
|
|
|
+ if (codec_dai->driver->ops->hw_free)
|
|
|
codec_dai->driver->ops->hw_free(substream, codec_dai);
|
|
|
codec_dai->rate = 0;
|
|
|
}
|
|
|
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
|
|
|
+ if (rtd->dai_link->ops->hw_free)
|
|
|
rtd->dai_link->ops->hw_free(substream);
|
|
|
|
|
|
mutex_unlock(&rtd->pcm_mutex);
|
|
@@ -963,6 +1086,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
|
@@ -995,21 +1120,36 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
|
}
|
|
|
|
|
|
/* free any machine hw params */
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
|
|
|
+ if (rtd->dai_link->ops->hw_free)
|
|
|
rtd->dai_link->ops->hw_free(substream);
|
|
|
|
|
|
/* free any DMA resources */
|
|
|
- if (platform->driver->ops && platform->driver->ops->hw_free)
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->hw_free)
|
|
|
platform->driver->ops->hw_free(substream);
|
|
|
|
|
|
+ /* free any component resources */
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->hw_free)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ component->driver->ops->hw_free(substream);
|
|
|
+ }
|
|
|
+
|
|
|
/* now free hw params for the DAIs */
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
|
|
|
+ if (codec_dai->driver->ops->hw_free)
|
|
|
codec_dai->driver->ops->hw_free(substream, codec_dai);
|
|
|
}
|
|
|
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
|
|
|
+ if (cpu_dai->driver->ops->hw_free)
|
|
|
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
|
|
|
|
|
|
mutex_unlock(&rtd->pcm_mutex);
|
|
@@ -1020,13 +1160,15 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
int i, ret;
|
|
|
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
|
|
|
+ if (codec_dai->driver->ops->trigger) {
|
|
|
ret = codec_dai->driver->ops->trigger(substream,
|
|
|
cmd, codec_dai);
|
|
|
if (ret < 0)
|
|
@@ -1034,19 +1176,35 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->trigger) {
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->trigger) {
|
|
|
ret = platform->driver->ops->trigger(substream, cmd);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) {
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->trigger)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = component->driver->ops->trigger(substream, cmd);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cpu_dai->driver->ops->trigger) {
|
|
|
ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) {
|
|
|
+ if (rtd->dai_link->ops->trigger) {
|
|
|
ret = rtd->dai_link->ops->trigger(substream, cmd);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
@@ -1065,8 +1223,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
|
|
|
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops &&
|
|
|
- codec_dai->driver->ops->bespoke_trigger) {
|
|
|
+ if (codec_dai->driver->ops->bespoke_trigger) {
|
|
|
ret = codec_dai->driver->ops->bespoke_trigger(substream,
|
|
|
cmd, codec_dai);
|
|
|
if (ret < 0)
|
|
@@ -1074,7 +1231,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) {
|
|
|
+ if (cpu_dai->driver->ops->bespoke_trigger) {
|
|
|
ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
@@ -1090,6 +1247,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
@@ -1098,15 +1257,31 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
|
|
|
snd_pcm_sframes_t codec_delay = 0;
|
|
|
int i;
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->pointer)
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->pointer)
|
|
|
offset = platform->driver->ops->pointer(substream);
|
|
|
|
|
|
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay)
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->pointer)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME: use 1st pointer */
|
|
|
+ offset = component->driver->ops->pointer(substream);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cpu_dai->driver->ops->delay)
|
|
|
delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
|
|
|
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dai = rtd->codec_dais[i];
|
|
|
- if (codec_dai->driver->ops && codec_dai->driver->ops->delay)
|
|
|
+ if (codec_dai->driver->ops->delay)
|
|
|
codec_delay = max(codec_delay,
|
|
|
codec_dai->driver->ops->delay(substream,
|
|
|
codec_dai));
|
|
@@ -2285,9 +2460,27 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
|
|
|
- if (platform->driver->ops && platform->driver->ops->ioctl)
|
|
|
+ if (platform && platform->driver->ops && platform->driver->ops->ioctl)
|
|
|
return platform->driver->ops->ioctl(substream, cmd, arg);
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ /* ignore duplication for now */
|
|
|
+ if (platform && (component == &platform->component))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->ioctl)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME: use 1st ioctl */
|
|
|
+ return component->driver->ops->ioctl(substream, cmd, arg);
|
|
|
+ }
|
|
|
+
|
|
|
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
|
|
}
|
|
|
|
|
@@ -2635,12 +2828,150 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
|
|
|
static void soc_pcm_private_free(struct snd_pcm *pcm)
|
|
|
{
|
|
|
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
|
|
|
- struct snd_soc_platform *platform = rtd->platform;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ /* need to sync the delayed work before releasing resources */
|
|
|
+
|
|
|
+ flush_delayed_work(&rtd->delayed_work);
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (component->pcm_free)
|
|
|
+ component->pcm_free(component, pcm);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_rtdcom_ack(struct snd_pcm_substream *substream)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->ack)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME. it returns 1st ask now */
|
|
|
+ return component->driver->ops->ack(substream);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel,
|
|
|
+ unsigned long pos, void __user *buf,
|
|
|
+ unsigned long bytes)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->copy_user)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME. it returns 1st copy now */
|
|
|
+ return component->driver->ops->copy_user(substream, channel,
|
|
|
+ pos, buf, bytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel,
|
|
|
+ unsigned long pos, void *buf, unsigned long bytes)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
|
|
|
- /* need to sync the delayed work before releasing resources */
|
|
|
- flush_delayed_work(&rtd->delayed_work);
|
|
|
- if (platform->driver->pcm_free)
|
|
|
- platform->driver->pcm_free(pcm);
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->copy_kernel)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME. it returns 1st copy now */
|
|
|
+ return component->driver->ops->copy_kernel(substream, channel,
|
|
|
+ pos, buf, bytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel,
|
|
|
+ unsigned long pos, unsigned long bytes)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->fill_silence)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME. it returns 1st silence now */
|
|
|
+ return component->driver->ops->fill_silence(substream, channel,
|
|
|
+ pos, bytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream,
|
|
|
+ unsigned long offset)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct page *page;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->page)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME. it returns 1st page now */
|
|
|
+ page = component->driver->ops->page(substream, offset);
|
|
|
+ if (page)
|
|
|
+ return page;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_rtdcom_mmap(struct snd_pcm_substream *substream,
|
|
|
+ struct vm_area_struct *vma)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (!component->driver->ops ||
|
|
|
+ !component->driver->ops->mmap)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* FIXME. it returns 1st mmap now */
|
|
|
+ return component->driver->ops->mmap(substream, vma);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
/* create a new pcm */
|
|
@@ -2649,6 +2980,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
+ struct snd_soc_component *component;
|
|
|
+ struct snd_soc_rtdcom_list *rtdcom;
|
|
|
struct snd_pcm *pcm;
|
|
|
char new_name[64];
|
|
|
int ret = 0, playback = 0, capture = 0;
|
|
@@ -2743,7 +3076,28 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
|
|
rtd->ops.ioctl = soc_pcm_ioctl;
|
|
|
}
|
|
|
|
|
|
- if (platform->driver->ops) {
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ const struct snd_pcm_ops *ops = rtdcom->component->driver->ops;
|
|
|
+
|
|
|
+ if (!ops)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (ops->ack)
|
|
|
+ rtd->ops.ack = soc_rtdcom_ack;
|
|
|
+ if (ops->copy_user)
|
|
|
+ rtd->ops.copy_user = soc_rtdcom_copy_user;
|
|
|
+ if (ops->copy_kernel)
|
|
|
+ rtd->ops.copy_kernel = soc_rtdcom_copy_kernel;
|
|
|
+ if (ops->fill_silence)
|
|
|
+ rtd->ops.fill_silence = soc_rtdcom_fill_silence;
|
|
|
+ if (ops->page)
|
|
|
+ rtd->ops.page = soc_rtdcom_page;
|
|
|
+ if (ops->mmap)
|
|
|
+ rtd->ops.mmap = soc_rtdcom_mmap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* overwrite */
|
|
|
+ if (platform && platform->driver->ops) {
|
|
|
rtd->ops.ack = platform->driver->ops->ack;
|
|
|
rtd->ops.copy_user = platform->driver->ops->copy_user;
|
|
|
rtd->ops.copy_kernel = platform->driver->ops->copy_kernel;
|
|
@@ -2758,10 +3112,15 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
|
|
if (capture)
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
|
|
|
|
|
|
- if (platform->driver->pcm_new) {
|
|
|
- ret = platform->driver->pcm_new(rtd);
|
|
|
+ for_each_rtdcom(rtd, rtdcom) {
|
|
|
+ component = rtdcom->component;
|
|
|
+
|
|
|
+ if (!component->pcm_new)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = component->pcm_new(component, rtd);
|
|
|
if (ret < 0) {
|
|
|
- dev_err(platform->dev,
|
|
|
+ dev_err(component->dev,
|
|
|
"ASoC: pcm constructor failed: %d\n",
|
|
|
ret);
|
|
|
return ret;
|