|
|
|
@ -28,6 +28,174 @@
|
|
|
|
|
|
|
|
|
|
#define DPCM_MAX_BE_USERS 8
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
|
|
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
case SND_SOC_DPCM_STATE_NEW:
|
|
|
|
|
return "new";
|
|
|
|
|
case SND_SOC_DPCM_STATE_OPEN:
|
|
|
|
|
return "open";
|
|
|
|
|
case SND_SOC_DPCM_STATE_HW_PARAMS:
|
|
|
|
|
return "hw_params";
|
|
|
|
|
case SND_SOC_DPCM_STATE_PREPARE:
|
|
|
|
|
return "prepare";
|
|
|
|
|
case SND_SOC_DPCM_STATE_START:
|
|
|
|
|
return "start";
|
|
|
|
|
case SND_SOC_DPCM_STATE_STOP:
|
|
|
|
|
return "stop";
|
|
|
|
|
case SND_SOC_DPCM_STATE_SUSPEND:
|
|
|
|
|
return "suspend";
|
|
|
|
|
case SND_SOC_DPCM_STATE_PAUSED:
|
|
|
|
|
return "paused";
|
|
|
|
|
case SND_SOC_DPCM_STATE_HW_FREE:
|
|
|
|
|
return "hw_free";
|
|
|
|
|
case SND_SOC_DPCM_STATE_CLOSE:
|
|
|
|
|
return "close";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
|
|
|
|
|
int stream, char *buf, size_t size)
|
|
|
|
|
{
|
|
|
|
|
struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
|
|
|
|
|
struct snd_soc_dpcm *dpcm;
|
|
|
|
|
ssize_t offset = 0;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
/* FE state */
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
"[%s - %s]\n", fe->dai_link->name,
|
|
|
|
|
stream ? "Capture" : "Playback");
|
|
|
|
|
|
|
|
|
|
offset += snprintf(buf + offset, size - offset, "State: %s\n",
|
|
|
|
|
dpcm_state_string(fe->dpcm[stream].state));
|
|
|
|
|
|
|
|
|
|
if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
|
|
|
|
|
(fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
"Hardware Params: "
|
|
|
|
|
"Format = %s, Channels = %d, Rate = %d\n",
|
|
|
|
|
snd_pcm_format_name(params_format(params)),
|
|
|
|
|
params_channels(params),
|
|
|
|
|
params_rate(params));
|
|
|
|
|
|
|
|
|
|
/* BEs state */
|
|
|
|
|
offset += snprintf(buf + offset, size - offset, "Backends:\n");
|
|
|
|
|
|
|
|
|
|
if (list_empty(&fe->dpcm[stream].be_clients)) {
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
" No active DSP links\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
|
|
|
|
|
for_each_dpcm_be(fe, stream, dpcm) {
|
|
|
|
|
struct snd_soc_pcm_runtime *be = dpcm->be;
|
|
|
|
|
params = &dpcm->hw_params;
|
|
|
|
|
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
"- %s\n", be->dai_link->name);
|
|
|
|
|
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
" State: %s\n",
|
|
|
|
|
dpcm_state_string(be->dpcm[stream].state));
|
|
|
|
|
|
|
|
|
|
if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
|
|
|
|
|
(be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
" Hardware Params: "
|
|
|
|
|
"Format = %s, Channels = %d, Rate = %d\n",
|
|
|
|
|
snd_pcm_format_name(params_format(params)),
|
|
|
|
|
params_channels(params),
|
|
|
|
|
params_rate(params));
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
|
|
|
|
|
out:
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
struct snd_soc_pcm_runtime *fe = file->private_data;
|
|
|
|
|
ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
|
|
|
|
|
int stream;
|
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
|
|
buf = kmalloc(out_count, GFP_KERNEL);
|
|
|
|
|
if (!buf)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
for_each_pcm_streams(stream)
|
|
|
|
|
if (snd_soc_dai_stream_valid(fe->cpu_dai, stream))
|
|
|
|
|
offset += dpcm_show_state(fe, stream,
|
|
|
|
|
buf + offset,
|
|
|
|
|
out_count - offset);
|
|
|
|
|
|
|
|
|
|
ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
|
|
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct file_operations dpcm_state_fops = {
|
|
|
|
|
.open = simple_open,
|
|
|
|
|
.read = dpcm_state_read_file,
|
|
|
|
|
.llseek = default_llseek,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
|
|
|
|
|
{
|
|
|
|
|
if (!rtd->dai_link)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!rtd->dai_link->dynamic)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!rtd->card->debugfs_card_root)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
|
|
|
|
|
rtd->card->debugfs_card_root);
|
|
|
|
|
|
|
|
|
|
debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
|
|
|
|
|
rtd, &dpcm_state_fops);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm, int stream)
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
|
|
name = kasprintf(GFP_KERNEL, "%s:%s", dpcm->be->dai_link->name,
|
|
|
|
|
stream ? "capture" : "playback");
|
|
|
|
|
if (name) {
|
|
|
|
|
dpcm->debugfs_state = debugfs_create_dir(
|
|
|
|
|
name, dpcm->fe->debugfs_dpcm_root);
|
|
|
|
|
debugfs_create_u32("state", 0644, dpcm->debugfs_state,
|
|
|
|
|
&dpcm->state);
|
|
|
|
|
kfree(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm)
|
|
|
|
|
{
|
|
|
|
|
debugfs_remove_recursive(dpcm->debugfs_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
static inline void dpcm_create_debugfs_state(struct snd_soc_dpcm *dpcm,
|
|
|
|
|
int stream)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void dpcm_remove_debugfs_state(struct snd_soc_dpcm *dpcm)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int soc_rtd_startup(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
|
struct snd_pcm_substream *substream)
|
|
|
|
|
{
|
|
|
|
@ -82,15 +250,6 @@ static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline
|
|
|
|
|
struct snd_soc_dapm_widget *dai_get_widget(struct snd_soc_dai *dai, int stream)
|
|
|
|
|
{
|
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
return dai->playback_widget;
|
|
|
|
|
else
|
|
|
|
|
return dai->capture_widget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
|
|
|
|
|
int stream, int action)
|
|
|
|
|
{
|
|
|
|
@ -363,29 +522,24 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
|
|
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
|
|
struct snd_soc_pcm_stream *pcm_codec, *pcm_cpu;
|
|
|
|
|
int stream = substream->stream;
|
|
|
|
|
int i;
|
|
|
|
|
unsigned int bits = 0, cpu_bits;
|
|
|
|
|
|
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
|
|
for_each_rtd_codec_dai(rtd, i, codec_dai) {
|
|
|
|
|
if (codec_dai->driver->playback.sig_bits == 0) {
|
|
|
|
|
bits = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
bits = max(codec_dai->driver->playback.sig_bits, bits);
|
|
|
|
|
for_each_rtd_codec_dai(rtd, i, codec_dai) {
|
|
|
|
|
pcm_codec = snd_soc_dai_get_pcm_stream(codec_dai, stream);
|
|
|
|
|
|
|
|
|
|
if (pcm_codec->sig_bits == 0) {
|
|
|
|
|
bits = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
cpu_bits = cpu_dai->driver->playback.sig_bits;
|
|
|
|
|
} else {
|
|
|
|
|
for_each_rtd_codec_dai(rtd, i, codec_dai) {
|
|
|
|
|
if (codec_dai->driver->capture.sig_bits == 0) {
|
|
|
|
|
bits = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
bits = max(codec_dai->driver->capture.sig_bits, bits);
|
|
|
|
|
}
|
|
|
|
|
cpu_bits = cpu_dai->driver->capture.sig_bits;
|
|
|
|
|
bits = max(pcm_codec->sig_bits, bits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pcm_cpu = snd_soc_dai_get_pcm_stream(cpu_dai, stream);
|
|
|
|
|
cpu_bits = pcm_cpu->sig_bits;
|
|
|
|
|
|
|
|
|
|
soc_pcm_set_msb(substream, bits);
|
|
|
|
|
soc_pcm_set_msb(substream, cpu_bits);
|
|
|
|
|
}
|
|
|
|
@ -396,20 +550,16 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
|
|
|
|
|
struct snd_pcm_hardware *hw = &runtime->hw;
|
|
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
|
|
struct snd_soc_dai *codec_dai;
|
|
|
|
|
struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver;
|
|
|
|
|
struct snd_soc_dai_driver *codec_dai_drv;
|
|
|
|
|
struct snd_soc_pcm_stream *codec_stream;
|
|
|
|
|
struct snd_soc_pcm_stream *cpu_stream;
|
|
|
|
|
unsigned int chan_min = 0, chan_max = UINT_MAX;
|
|
|
|
|
unsigned int rate_min = 0, rate_max = UINT_MAX;
|
|
|
|
|
unsigned int rates = UINT_MAX;
|
|
|
|
|
u64 formats = ULLONG_MAX;
|
|
|
|
|
int stream = substream->stream;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
cpu_stream = &cpu_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
cpu_stream = &cpu_dai_drv->capture;
|
|
|
|
|
cpu_stream = snd_soc_dai_get_pcm_stream(rtd->cpu_dai, stream);
|
|
|
|
|
|
|
|
|
|
/* first calculate min/max only for CODECs in the DAI link */
|
|
|
|
|
for_each_rtd_codec_dai(rtd, i, codec_dai) {
|
|
|
|
@ -427,11 +577,8 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
|
|
|
|
|
substream->stream))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
codec_dai_drv = codec_dai->driver;
|
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
codec_stream = &codec_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
codec_stream = &codec_dai_drv->capture;
|
|
|
|
|
codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream);
|
|
|
|
|
|
|
|
|
|
chan_min = max(chan_min, codec_stream->channels_min);
|
|
|
|
|
chan_max = min(chan_max, codec_stream->channels_max);
|
|
|
|
|
rate_min = max(rate_min, codec_stream->rate_min);
|
|
|
|
@ -1152,9 +1299,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
|
|
|
|
|
{
|
|
|
|
|
struct snd_soc_dpcm *dpcm;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
char *name;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* only add new dpcms */
|
|
|
|
|
for_each_dpcm_be(fe, stream, dpcm) {
|
|
|
|
@ -1179,17 +1323,8 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
|
|
|
|
|
stream ? "capture" : "playback", fe->dai_link->name,
|
|
|
|
|
stream ? "<-" : "->", be->dai_link->name);
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
name = kasprintf(GFP_KERNEL, "%s:%s", be->dai_link->name,
|
|
|
|
|
stream ? "capture" : "playback");
|
|
|
|
|
if (name) {
|
|
|
|
|
dpcm->debugfs_state = debugfs_create_dir(name,
|
|
|
|
|
fe->debugfs_dpcm_root);
|
|
|
|
|
debugfs_create_u32("state", 0644, dpcm->debugfs_state,
|
|
|
|
|
&dpcm->state);
|
|
|
|
|
kfree(name);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
dpcm_create_debugfs_state(dpcm, stream);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1242,9 +1377,8 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
|
|
|
|
|
/* BEs still alive need new FE */
|
|
|
|
|
dpcm_be_reparent(fe, dpcm->be, stream);
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
debugfs_remove_recursive(dpcm->debugfs_state);
|
|
|
|
|
#endif
|
|
|
|
|
dpcm_remove_debugfs_state(dpcm);
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
|
|
|
|
|
list_del(&dpcm->list_be);
|
|
|
|
|
list_del(&dpcm->list_fe);
|
|
|
|
@ -1269,7 +1403,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
|
|
|
|
|
if (!be->dai_link->no_pcm)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
w = dai_get_widget(be->cpu_dai, stream);
|
|
|
|
|
w = snd_soc_dai_get_widget(be->cpu_dai, stream);
|
|
|
|
|
|
|
|
|
|
dev_dbg(card->dev, "ASoC: try BE : %s\n",
|
|
|
|
|
w ? w->name : "(not set)");
|
|
|
|
@ -1278,7 +1412,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
|
|
|
|
|
return be;
|
|
|
|
|
|
|
|
|
|
for_each_rtd_codec_dai(be, i, dai) {
|
|
|
|
|
w = dai_get_widget(dai, stream);
|
|
|
|
|
w = snd_soc_dai_get_widget(dai, stream);
|
|
|
|
|
|
|
|
|
|
if (w == widget)
|
|
|
|
|
return be;
|
|
|
|
@ -1338,6 +1472,11 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
|
|
|
|
|
return paths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dpcm_path_put(struct snd_soc_dapm_widget_list **list)
|
|
|
|
|
{
|
|
|
|
|
snd_soc_dapm_dai_free_widgets(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
|
|
|
|
|
struct snd_soc_dapm_widget_list **list_)
|
|
|
|
|
{
|
|
|
|
@ -1353,7 +1492,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
/* is there a valid CPU DAI widget for this BE */
|
|
|
|
|
widget = dai_get_widget(dpcm->be->cpu_dai, stream);
|
|
|
|
|
widget = snd_soc_dai_get_widget(dpcm->be->cpu_dai, stream);
|
|
|
|
|
|
|
|
|
|
/* prune the BE if it's no longer in our active list */
|
|
|
|
|
if (widget && widget_in_list(list, widget))
|
|
|
|
@ -1362,7 +1501,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
|
|
|
|
|
/* is there a valid CODEC DAI widget for this BE */
|
|
|
|
|
do_prune = 1;
|
|
|
|
|
for_each_rtd_codec_dai(dpcm->be, i, dai) {
|
|
|
|
|
widget = dai_get_widget(dai, stream);
|
|
|
|
|
widget = snd_soc_dai_get_widget(dai, stream);
|
|
|
|
|
|
|
|
|
|
/* prune the BE if it's no longer in our active list */
|
|
|
|
|
if (widget && widget_in_list(list, widget))
|
|
|
|
@ -1615,7 +1754,6 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
|
|
|
|
|
|
|
|
|
|
for_each_dpcm_be(fe, stream, dpcm) {
|
|
|
|
|
struct snd_soc_pcm_runtime *be = dpcm->be;
|
|
|
|
|
struct snd_soc_dai_driver *codec_dai_drv;
|
|
|
|
|
struct snd_soc_pcm_stream *codec_stream;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
@ -1627,11 +1765,7 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
|
|
|
|
|
if (!snd_soc_dai_stream_valid(dai, stream))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
codec_dai_drv = dai->driver;
|
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
codec_stream = &codec_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
codec_stream = &codec_dai_drv->capture;
|
|
|
|
|
codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
|
|
|
|
|
|
|
|
|
|
*formats &= codec_stream->formats;
|
|
|
|
|
}
|
|
|
|
@ -1656,15 +1790,10 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
|
|
|
|
|
|
|
|
|
|
for_each_dpcm_be(fe, stream, dpcm) {
|
|
|
|
|
struct snd_soc_pcm_runtime *be = dpcm->be;
|
|
|
|
|
struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver;
|
|
|
|
|
struct snd_soc_dai_driver *codec_dai_drv;
|
|
|
|
|
struct snd_soc_pcm_stream *codec_stream;
|
|
|
|
|
struct snd_soc_pcm_stream *cpu_stream;
|
|
|
|
|
|
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
cpu_stream = &cpu_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
cpu_stream = &cpu_dai_drv->capture;
|
|
|
|
|
cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream);
|
|
|
|
|
|
|
|
|
|
*channels_min = max(*channels_min, cpu_stream->channels_min);
|
|
|
|
|
*channels_max = min(*channels_max, cpu_stream->channels_max);
|
|
|
|
@ -1674,12 +1803,7 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
|
|
|
|
|
* DAIs connected to a single CPU DAI, use CPU DAI's directly
|
|
|
|
|
*/
|
|
|
|
|
if (be->num_codecs == 1) {
|
|
|
|
|
codec_dai_drv = be->codec_dais[0]->driver;
|
|
|
|
|
|
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
codec_stream = &codec_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
codec_stream = &codec_dai_drv->capture;
|
|
|
|
|
codec_stream = snd_soc_dai_get_pcm_stream(be->codec_dais[0], stream);
|
|
|
|
|
|
|
|
|
|
*channels_min = max(*channels_min,
|
|
|
|
|
codec_stream->channels_min);
|
|
|
|
@ -1708,17 +1832,12 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
|
|
|
|
|
|
|
|
|
|
for_each_dpcm_be(fe, stream, dpcm) {
|
|
|
|
|
struct snd_soc_pcm_runtime *be = dpcm->be;
|
|
|
|
|
struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver;
|
|
|
|
|
struct snd_soc_dai_driver *codec_dai_drv;
|
|
|
|
|
struct snd_soc_pcm_stream *codec_stream;
|
|
|
|
|
struct snd_soc_pcm_stream *cpu_stream;
|
|
|
|
|
struct snd_soc_dai *dai;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
cpu_stream = &cpu_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
cpu_stream = &cpu_dai_drv->capture;
|
|
|
|
|
cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream);
|
|
|
|
|
|
|
|
|
|
*rate_min = max(*rate_min, cpu_stream->rate_min);
|
|
|
|
|
*rate_max = min_not_zero(*rate_max, cpu_stream->rate_max);
|
|
|
|
@ -1732,11 +1851,7 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
|
|
|
|
|
if (!snd_soc_dai_stream_valid(dai, stream))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
codec_dai_drv = dai->driver;
|
|
|
|
|
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
|
|
|
codec_stream = &codec_dai_drv->playback;
|
|
|
|
|
else
|
|
|
|
|
codec_stream = &codec_dai_drv->capture;
|
|
|
|
|
codec_stream = snd_soc_dai_get_pcm_stream(dai, stream);
|
|
|
|
|
|
|
|
|
|
*rate_min = max(*rate_min, codec_stream->rate_min);
|
|
|
|
|
*rate_max = min_not_zero(*rate_max,
|
|
|
|
@ -2596,37 +2711,12 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
|
|
|
|
|
ret = dpcm_run_update_startup(fe, stream);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
|
|
|
|
|
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
|
|
|
|
|
ret = dpcm_run_update_shutdown(fe, stream);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
|
|
|
|
|
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
|
|
|
|
|
{
|
|
|
|
|
struct snd_soc_dapm_widget_list *list;
|
|
|
|
|
int stream;
|
|
|
|
|
int count, paths;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (!fe->dai_link->dynamic)
|
|
|
|
|
return 0;
|
|
|
|
@ -2663,10 +2753,14 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
|
|
|
|
|
/* update any playback/capture paths */
|
|
|
|
|
count = dpcm_process_paths(fe, stream, &list, new);
|
|
|
|
|
if (count) {
|
|
|
|
|
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
|
|
|
|
|
if (new)
|
|
|
|
|
dpcm_run_new_update(fe, stream);
|
|
|
|
|
ret = dpcm_run_update_startup(fe, stream);
|
|
|
|
|
else
|
|
|
|
|
dpcm_run_old_update(fe, stream);
|
|
|
|
|
ret = dpcm_run_update_shutdown(fe, stream);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
|
|
|
|
|
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
|
|
|
|
|
|
|
|
|
|
dpcm_clear_pending_state(fe, stream);
|
|
|
|
|
dpcm_be_disconnect(fe, stream);
|
|
|
|
@ -3006,141 +3100,3 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
|
|
|
|
|
return snd_soc_dpcm_check_state(fe, be, stream, state, ARRAY_SIZE(state));
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
|
static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
|
|
|
|
|
{
|
|
|
|
|
switch (state) {
|
|
|
|
|
case SND_SOC_DPCM_STATE_NEW:
|
|
|
|
|
return "new";
|
|
|
|
|
case SND_SOC_DPCM_STATE_OPEN:
|
|
|
|
|
return "open";
|
|
|
|
|
case SND_SOC_DPCM_STATE_HW_PARAMS:
|
|
|
|
|
return "hw_params";
|
|
|
|
|
case SND_SOC_DPCM_STATE_PREPARE:
|
|
|
|
|
return "prepare";
|
|
|
|
|
case SND_SOC_DPCM_STATE_START:
|
|
|
|
|
return "start";
|
|
|
|
|
case SND_SOC_DPCM_STATE_STOP:
|
|
|
|
|
return "stop";
|
|
|
|
|
case SND_SOC_DPCM_STATE_SUSPEND:
|
|
|
|
|
return "suspend";
|
|
|
|
|
case SND_SOC_DPCM_STATE_PAUSED:
|
|
|
|
|
return "paused";
|
|
|
|
|
case SND_SOC_DPCM_STATE_HW_FREE:
|
|
|
|
|
return "hw_free";
|
|
|
|
|
case SND_SOC_DPCM_STATE_CLOSE:
|
|
|
|
|
return "close";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
|
|
|
|
|
int stream, char *buf, size_t size)
|
|
|
|
|
{
|
|
|
|
|
struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
|
|
|
|
|
struct snd_soc_dpcm *dpcm;
|
|
|
|
|
ssize_t offset = 0;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
/* FE state */
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
"[%s - %s]\n", fe->dai_link->name,
|
|
|
|
|
stream ? "Capture" : "Playback");
|
|
|
|
|
|
|
|
|
|
offset += snprintf(buf + offset, size - offset, "State: %s\n",
|
|
|
|
|
dpcm_state_string(fe->dpcm[stream].state));
|
|
|
|
|
|
|
|
|
|
if ((fe->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
|
|
|
|
|
(fe->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
"Hardware Params: "
|
|
|
|
|
"Format = %s, Channels = %d, Rate = %d\n",
|
|
|
|
|
snd_pcm_format_name(params_format(params)),
|
|
|
|
|
params_channels(params),
|
|
|
|
|
params_rate(params));
|
|
|
|
|
|
|
|
|
|
/* BEs state */
|
|
|
|
|
offset += snprintf(buf + offset, size - offset, "Backends:\n");
|
|
|
|
|
|
|
|
|
|
if (list_empty(&fe->dpcm[stream].be_clients)) {
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
" No active DSP links\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
|
|
|
|
|
for_each_dpcm_be(fe, stream, dpcm) {
|
|
|
|
|
struct snd_soc_pcm_runtime *be = dpcm->be;
|
|
|
|
|
params = &dpcm->hw_params;
|
|
|
|
|
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
"- %s\n", be->dai_link->name);
|
|
|
|
|
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
" State: %s\n",
|
|
|
|
|
dpcm_state_string(be->dpcm[stream].state));
|
|
|
|
|
|
|
|
|
|
if ((be->dpcm[stream].state >= SND_SOC_DPCM_STATE_HW_PARAMS) &&
|
|
|
|
|
(be->dpcm[stream].state <= SND_SOC_DPCM_STATE_STOP))
|
|
|
|
|
offset += snprintf(buf + offset, size - offset,
|
|
|
|
|
" Hardware Params: "
|
|
|
|
|
"Format = %s, Channels = %d, Rate = %d\n",
|
|
|
|
|
snd_pcm_format_name(params_format(params)),
|
|
|
|
|
params_channels(params),
|
|
|
|
|
params_rate(params));
|
|
|
|
|
}
|
|
|
|
|
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
|
|
|
|
|
out:
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
struct snd_soc_pcm_runtime *fe = file->private_data;
|
|
|
|
|
ssize_t out_count = PAGE_SIZE, offset = 0, ret = 0;
|
|
|
|
|
int stream;
|
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
|
|
buf = kmalloc(out_count, GFP_KERNEL);
|
|
|
|
|
if (!buf)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
for_each_pcm_streams(stream)
|
|
|
|
|
if (snd_soc_dai_stream_valid(fe->cpu_dai, stream))
|
|
|
|
|
offset += dpcm_show_state(fe, stream,
|
|
|
|
|
buf + offset,
|
|
|
|
|
out_count - offset);
|
|
|
|
|
|
|
|
|
|
ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
|
|
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct file_operations dpcm_state_fops = {
|
|
|
|
|
.open = simple_open,
|
|
|
|
|
.read = dpcm_state_read_file,
|
|
|
|
|
.llseek = default_llseek,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
|
|
|
|
|
{
|
|
|
|
|
if (!rtd->dai_link)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!rtd->dai_link->dynamic)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!rtd->card->debugfs_card_root)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
|
|
|
|
|
rtd->card->debugfs_card_root);
|
|
|
|
|
|
|
|
|
|
debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root,
|
|
|
|
|
rtd, &dpcm_state_fops);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|