diff options
Diffstat (limited to 'sound/soc/soc-dai.c')
-rw-r--r-- | sound/soc/soc-dai.c | 485 |
1 files changed, 373 insertions, 112 deletions
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 31c41559034b..b05e18b63a1c 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -8,6 +8,29 @@ #include <sound/soc.h> #include <sound/soc-dai.h> +#include <sound/soc-link.h> + +#define soc_dai_ret(dai, ret) _soc_dai_ret(dai, __func__, ret) +static inline int _soc_dai_ret(struct snd_soc_dai *dai, + const char *func, int ret) +{ + /* Positive, Zero values are not errors */ + if (ret >= 0) + return ret; + + /* Negative values might be errors */ + switch (ret) { + case -EPROBE_DEFER: + case -ENOTSUPP: + break; + default: + dev_err(dai->dev, + "ASoC: error at %s on %s: %d\n", + func, dai->name, ret); + } + + return ret; +} /** * snd_soc_dai_set_sysclk - configure DAI system or master clock. @@ -21,11 +44,16 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - if (dai->driver->ops->set_sysclk) - return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + int ret; + + if (dai->driver->ops && + dai->driver->ops->set_sysclk) + ret = dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + else + ret = snd_soc_component_set_sysclk(dai->component, clk_id, 0, + freq, dir); - return snd_soc_component_set_sysclk(dai->component, clk_id, 0, - freq, dir); + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); @@ -42,10 +70,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { - if (dai->driver->ops->set_clkdiv) - return dai->driver->ops->set_clkdiv(dai, div_id, div); - else - return -EINVAL; + int ret = -EINVAL; + + if (dai->driver->ops && + dai->driver->ops->set_clkdiv) + ret = dai->driver->ops->set_clkdiv(dai, div_id, div); + + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); @@ -62,12 +93,17 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - if (dai->driver->ops->set_pll) - return dai->driver->ops->set_pll(dai, pll_id, source, - freq_in, freq_out); + int ret; - return snd_soc_component_set_pll(dai->component, pll_id, source, - freq_in, freq_out); + if (dai->driver->ops && + dai->driver->ops->set_pll) + ret = dai->driver->ops->set_pll(dai, pll_id, source, + freq_in, freq_out); + else + ret = snd_soc_component_set_pll(dai->component, pll_id, source, + freq_in, freq_out); + + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); @@ -80,10 +116,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); */ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { - if (dai->driver->ops->set_bclk_ratio) - return dai->driver->ops->set_bclk_ratio(dai, ratio); - else - return -EINVAL; + int ret = -EINVAL; + + if (dai->driver->ops && + dai->driver->ops->set_bclk_ratio) + ret = dai->driver->ops->set_bclk_ratio(dai, ratio); + + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); @@ -96,9 +135,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); */ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - if (dai->driver->ops->set_fmt == NULL) - return -ENOTSUPP; - return dai->driver->ops->set_fmt(dai, fmt); + int ret = -ENOTSUPP; + + if (dai->driver->ops && + dai->driver->ops->set_fmt) + ret = dai->driver->ops->set_fmt(dai, fmt); + + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); @@ -153,7 +196,10 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - if (dai->driver->ops->xlate_tdm_slot_mask) + int ret = -ENOTSUPP; + + if (dai->driver->ops && + dai->driver->ops->xlate_tdm_slot_mask) dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); else @@ -162,11 +208,11 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, dai->tx_mask = tx_mask; dai->rx_mask = rx_mask; - if (dai->driver->ops->set_tdm_slot) - return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, + if (dai->driver->ops && + dai->driver->ops->set_tdm_slot) + ret = dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); - else - return -ENOTSUPP; + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); @@ -186,11 +232,13 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot) { - if (dai->driver->ops->set_channel_map) - return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, - rx_num, rx_slot); - else - return -ENOTSUPP; + int ret = -ENOTSUPP; + + if (dai->driver->ops && + dai->driver->ops->set_channel_map) + ret = dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, + rx_num, rx_slot); + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); @@ -208,11 +256,13 @@ int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { - if (dai->driver->ops->get_channel_map) - return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, - rx_num, rx_slot); - else - return -ENOTSUPP; + int ret = -ENOTSUPP; + + if (dai->driver->ops && + dai->driver->ops->get_channel_map) + ret = dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, + rx_num, rx_slot); + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); @@ -225,10 +275,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); */ int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) { - if (dai->driver->ops->set_tristate) - return dai->driver->ops->set_tristate(dai, tristate); - else - return -EINVAL; + int ret = -EINVAL; + + if (dai->driver->ops && + dai->driver->ops->set_tristate) + ret = dai->driver->ops->set_tristate(dai, tristate); + + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); @@ -243,13 +296,17 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, int direction) { - if (dai->driver->ops->mute_stream) - return dai->driver->ops->mute_stream(dai, mute, direction); + int ret = -ENOTSUPP; + + if (dai->driver->ops && + dai->driver->ops->mute_stream) + ret = dai->driver->ops->mute_stream(dai, mute, direction); else if (direction == SNDRV_PCM_STREAM_PLAYBACK && + dai->driver->ops && dai->driver->ops->digital_mute) - return dai->driver->ops->digital_mute(dai, mute); - else - return -ENOTSUPP; + ret = dai->driver->ops->digital_mute(dai, mute); + + return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); @@ -258,35 +315,25 @@ int snd_soc_dai_hw_params(struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - int ret; + int ret = 0; /* perform any topology hw_params fixups before DAI */ - if (rtd->dai_link->be_hw_params_fixup) { - ret = rtd->dai_link->be_hw_params_fixup(rtd, params); - if (ret < 0) { - dev_err(rtd->dev, - "ASoC: hw_params topology fixup failed %d\n", - ret); - return ret; - } - } + ret = snd_soc_link_be_hw_params_fixup(rtd, params); + if (ret < 0) + goto end; - if (dai->driver->ops->hw_params) { + if (dai->driver->ops && + 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", - dai->name, ret); - return ret; - } - } - - return 0; +end: + return soc_dai_ret(dai, ret); } void snd_soc_dai_hw_free(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { - if (dai->driver->ops->hw_free) + if (dai->driver->ops && + dai->driver->ops->hw_free) dai->driver->ops->hw_free(substream, dai); } @@ -295,96 +342,310 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, { int ret = 0; - if (dai->driver->ops->startup) + if (dai->driver->ops && + dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); - return ret; + return soc_dai_ret(dai, ret); } void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { - if (dai->driver->ops->shutdown) + if (dai->driver->ops && + dai->driver->ops->shutdown) dai->driver->ops->shutdown(substream, dai); } -int snd_soc_dai_prepare(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) +snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, + struct snd_pcm_substream *substream) { - int ret = 0; + int delay = 0; - if (dai->driver->ops->prepare) - ret = dai->driver->ops->prepare(substream, dai); + if (dai->driver->ops && + dai->driver->ops->delay) + delay = dai->driver->ops->delay(substream, dai); - return ret; + return delay; } -int snd_soc_dai_trigger(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream, - int cmd) +int snd_soc_dai_compress_new(struct snd_soc_dai *dai, + struct snd_soc_pcm_runtime *rtd, int num) { - int ret = 0; + int ret = -ENOTSUPP; + if (dai->driver->compress_new) + ret = dai->driver->compress_new(rtd, num); + return soc_dai_ret(dai, ret); +} + +/* + * snd_soc_dai_stream_valid() - check if a DAI supports the given stream + * + * Returns true if the DAI supports the indicated stream type. + */ +bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) +{ + struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); - if (dai->driver->ops->trigger) - ret = dai->driver->ops->trigger(substream, cmd, dai); + /* If the codec specifies any channels at all, it supports the stream */ + return stream->channels_min; +} - return ret; +void snd_soc_dai_action(struct snd_soc_dai *dai, + int stream, int action) +{ + /* see snd_soc_dai_stream_active() */ + dai->stream_active[stream] += action; + + /* see snd_soc_component_active() */ + dai->component->active += action; } +EXPORT_SYMBOL_GPL(snd_soc_dai_action); -int snd_soc_dai_bespoke_trigger(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream, - int cmd) +int snd_soc_dai_active(struct snd_soc_dai *dai) { - int ret = 0; + int stream, active; + + active = 0; + for_each_pcm_streams(stream) + active += dai->stream_active[stream]; + + return active; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_active); + +int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order) +{ + struct snd_soc_dai *dai; + int i; + + for_each_rtd_dais(rtd, i, dai) { + if (dai->driver->probe_order != order) + continue; + + if (dai->driver->probe) { + int ret = dai->driver->probe(dai); + + if (ret < 0) + return soc_dai_ret(dai, ret); + } + + dai->probed = 1; + } + + return 0; +} + +int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order) +{ + struct snd_soc_dai *dai; + int i, r, ret = 0; + + for_each_rtd_dais(rtd, i, dai) { + if (dai->driver->remove_order != order) + continue; + + if (dai->probed && + dai->driver->remove) { + r = dai->driver->remove(dai); + if (r < 0) + ret = r; /* use last error */ + } - if (dai->driver->ops->bespoke_trigger) - ret = dai->driver->ops->bespoke_trigger(substream, cmd, dai); + dai->probed = 0; + } return ret; } -snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) +int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd) { - int delay = 0; + struct snd_soc_dai *dai; + int i, ret = 0; + + for_each_rtd_dais(rtd, i, dai) { + if (dai->driver->pcm_new) { + ret = dai->driver->pcm_new(rtd, dai); + if (ret < 0) + return soc_dai_ret(dai, ret); + } + } - if (dai->driver->ops->delay) - delay = dai->driver->ops->delay(substream, dai); + return 0; +} - return delay; +int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai; + int i, ret; + + for_each_rtd_dais(rtd, i, dai) { + if (dai->driver->ops && + dai->driver->ops->prepare) { + ret = dai->driver->ops->prepare(substream, dai); + if (ret < 0) + return soc_dai_ret(dai, ret); + } + } + + return 0; } -int snd_soc_dai_probe(struct snd_soc_dai *dai) +int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, + int cmd) { - if (dai->driver->probe) - return dai->driver->probe(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai; + int i, ret; + + for_each_rtd_dais(rtd, i, dai) { + if (dai->driver->ops && + dai->driver->ops->trigger) { + ret = dai->driver->ops->trigger(substream, cmd, dai); + if (ret < 0) + return soc_dai_ret(dai, ret); + } + } + return 0; } -int snd_soc_dai_remove(struct snd_soc_dai *dai) +int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, + int cmd) { - if (dai->driver->remove) - return dai->driver->remove(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai; + int i, ret; + + for_each_rtd_dais(rtd, i, dai) { + if (dai->driver->ops && + dai->driver->ops->bespoke_trigger) { + ret = dai->driver->ops->bespoke_trigger(substream, + cmd, dai); + if (ret < 0) + return soc_dai_ret(dai, ret); + } + } + return 0; } -int snd_soc_dai_compress_new(struct snd_soc_dai *dai, - struct snd_soc_pcm_runtime *rtd, int num) +int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream) { - if (dai->driver->compress_new) - return dai->driver->compress_new(rtd, num); - return -ENOTSUPP; + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->startup) + ret = dai->driver->cops->startup(cstream, dai); + + return soc_dai_ret(dai, ret); } +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_startup); -/* - * snd_soc_dai_stream_valid() - check if a DAI supports the given stream - * - * Returns true if the DAI supports the indicated stream type. - */ -bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) +void snd_soc_dai_compr_shutdown(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream) { - struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); + if (dai->driver->cops && + dai->driver->cops->shutdown) + dai->driver->cops->shutdown(cstream, dai); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_shutdown); - /* If the codec specifies any channels at all, it supports the stream */ - return stream->channels_min; +int snd_soc_dai_compr_trigger(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, int cmd) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->trigger) + ret = dai->driver->cops->trigger(cstream, cmd, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_trigger); + +int snd_soc_dai_compr_set_params(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->set_params) + ret = dai->driver->cops->set_params(cstream, params, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_params); + +int snd_soc_dai_compr_get_params(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, + struct snd_codec *params) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->get_params) + ret = dai->driver->cops->get_params(cstream, params, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_params); + +int snd_soc_dai_compr_ack(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, + size_t bytes) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->ack) + ret = dai->driver->cops->ack(cstream, bytes, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_ack); + +int snd_soc_dai_compr_pointer(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->pointer) + ret = dai->driver->cops->pointer(cstream, tstamp, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_pointer); + +int snd_soc_dai_compr_set_metadata(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->set_metadata) + ret = dai->driver->cops->set_metadata(cstream, metadata, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_set_metadata); + +int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, + struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + int ret = 0; + + if (dai->driver->cops && + dai->driver->cops->get_metadata) + ret = dai->driver->cops->get_metadata(cstream, metadata, dai); + + return soc_dai_ret(dai, ret); } +EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_metadata); |