| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Kuninori Morimoto | 3280 | 87.56% | 65 | 61.32% |
| Frank Mandarino | 75 | 2.00% | 1 | 0.94% |
| Mark Brown | 50 | 1.33% | 3 | 2.83% |
| Peter Suti | 43 | 1.15% | 1 | 0.94% |
| Liam Girdwood | 36 | 0.96% | 5 | 4.72% |
| James Calligeros | 34 | 0.91% | 1 | 0.94% |
| Srinivas Kandagatla | 30 | 0.80% | 1 | 0.94% |
| Troy Mitchell | 28 | 0.75% | 1 | 0.94% |
| Martin Blumenstingl | 25 | 0.67% | 1 | 0.94% |
| Stephen Warren | 19 | 0.51% | 1 | 0.94% |
| Shreyas NC | 16 | 0.43% | 1 | 0.94% |
| Richard Purdie | 16 | 0.43% | 1 | 0.94% |
| Vinod Koul | 13 | 0.35% | 1 | 0.94% |
| Namarta Kohli | 12 | 0.32% | 1 | 0.94% |
| Jeeja KP | 11 | 0.29% | 2 | 1.89% |
| Barry Song | 8 | 0.21% | 1 | 0.94% |
| Atsushi Nemoto | 8 | 0.21% | 1 | 0.94% |
| Krzysztof Kozlowski | 8 | 0.21% | 4 | 3.77% |
| Jie Yang | 6 | 0.16% | 1 | 0.94% |
| HariKrishna Sagala | 6 | 0.16% | 1 | 0.94% |
| Arnaud Pouliquen | 5 | 0.13% | 1 | 0.94% |
| Nicolin Chen | 4 | 0.11% | 1 | 0.94% |
| Jerome Brunet | 3 | 0.08% | 1 | 0.94% |
| 이경택 | 2 | 0.05% | 1 | 0.94% |
| Pierre-Louis Bossart | 2 | 0.05% | 2 | 1.89% |
| Ricard Wanderlöf | 1 | 0.03% | 1 | 0.94% |
| Martin Povišer | 1 | 0.03% | 1 | 0.94% |
| Joris Verhaegen | 1 | 0.03% | 1 | 0.94% |
| Charles Keepax | 1 | 0.03% | 1 | 0.94% |
| Eric Miao | 1 | 0.03% | 1 | 0.94% |
| John Watts | 1 | 0.03% | 1 | 0.94% |
| Total | 3746 | 106 |
// SPDX-License-Identifier: GPL-2.0 // // soc-dai.c // // Copyright (C) 2019 Renesas Electronics Corp. // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> // #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(const struct snd_soc_dai *dai, const char *func, int ret) { return snd_soc_ret(dai->dev, ret, "at %s() on %s\n", func, dai->name); } /* * We might want to check substream by using list. * In such case, we can update these macros. */ #define soc_dai_mark_push(dai, substream, tgt) ((dai)->mark_##tgt = substream) #define soc_dai_mark_pop(dai, tgt) ((dai)->mark_##tgt = NULL) #define soc_dai_mark_match(dai, substream, tgt) ((dai)->mark_##tgt == substream) /** * snd_soc_dai_set_sysclk - configure DAI system or master clock. * @dai: DAI * @clk_id: DAI specific clock ID * @freq: new clock frequency in Hz * @dir: new clock direction (SND_SOC_CLOCK_IN or SND_SOC_CLOCK_OUT) * * Configures the DAI master (MCLK) or system (SYSCLK) clocking. */ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int 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 soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); /** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI * @div_id: DAI specific clock divider ID * @div: new clock divisor. * * Configures the clock dividers. This is used to derive the best DAI bit and * frame clocks from the system or master clock. It's best to set the DAI bit * and frame clocks as low as possible to save system power. */ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { 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); /** * snd_soc_dai_set_pll - configure DAI PLL. * @dai: DAI * @pll_id: DAI specific PLL ID * @source: DAI specific source for the PLL * @freq_in: PLL input clock frequency in Hz * @freq_out: requested PLL output clock frequency in Hz * * Configures and enables PLL to generate output clock based on input clock. */ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { int ret; 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); /** * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. * @dai: DAI * @ratio: Ratio of BCLK to Sample rate. * * Configures the DAI for a preset BCLK to sample rate ratio. */ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { int ret = -ENOTSUPP; if (dai->driver->ops && dai->driver->ops->set_bclk_ratio) ret = dai->driver->ops->set_bclk_ratio(dai, ratio); if (!ret) dai->bclk_ratio = ratio; return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); /** * snd_soc_dai_set_bclk_clk - set the BCLK clock for shared clock detection * @dai: DAI * @bclk: BCLK clock pointer (or NULL to clear) * * When multiple DAIs share the same physical BCLK (detected via * clk_is_match()), the ASoC core will automatically constrain their * hw_params so that the resulting BCLK rates are compatible. */ void snd_soc_dai_set_bclk_clk(struct snd_soc_dai *dai, struct clk *bclk) { dai->bclk = bclk; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_clk); static int soc_dai_fmt_match_cnt(u64 fmt) { int cnt = 0; if (fmt & SND_SOC_POSSIBLE_DAIFMT_FORMAT_MASK) cnt++; if (fmt & SND_SOC_POSSIBLE_DAIFMT_CLOCK_MASK) cnt++; if (fmt & SND_SOC_POSSIBLE_DAIFMT_INV_MASK) cnt++; return cnt; } static void soc_dai_auto_select_format(u64 fmt, const struct snd_soc_pcm_runtime *rtd, int idx, u64 *best_fmt) { struct snd_soc_dai *dai; const struct snd_soc_dai_ops *ops; int max_idx = rtd->dai_link->num_cpus + rtd->dai_link->num_codecs; u64 available_fmt; /* * NOTE * It doesn't support Multi CPU/Codec for now */ if (rtd->dai_link->num_cpus != 1 || rtd->dai_link->num_codecs != 1) return; if (idx >= max_idx) return; dai = rtd->dais[idx]; ops = dai->driver->ops; /* zero chance of auto select format */ if (!ops || !ops->num_auto_selectable_formats) return; /* **************************** * NOTE **************************** * Using .auto_selectable_formats is not mandatory, * It try to find best formats as much as possible, but automatically selecting the * perfect format is impossible. So you can select full or missing format manually * from Sound Card. * * ex) * CPU Codec * (A)[0] I2S/LEFT_J : IB_NF/IB_IF (X)[0] I2S/DSP_A: NB_NF : GATED * (B)[1] DSP_A/DSP_B: NB_NF/IB_NF (Y)[1] LEFT_J: NB_NF : GATED * (C)[2] ... * * 1. (A) -> (X) : I2S :update best format * 2. (A) -> (Y) : LEFT_J * 3. (B) -> (X) : DSP_A/NB_NF :update best format * 4. (B) -> (Y) : NB_NF * 5. (C) -> (X) ... * 6. (C) -> (Y) ... * ... * * In above case GATED will not be selected */ /* find best formats */ for (int i = 0; i < ops->num_auto_selectable_formats; i++) { available_fmt = fmt & ops->auto_selectable_formats[i]; /* In case of last DAI */ if (idx + 1 >= max_idx) { int cnt1 = soc_dai_fmt_match_cnt(*best_fmt); int cnt2 = soc_dai_fmt_match_cnt(available_fmt); if (cnt1 < cnt2) *best_fmt = available_fmt; } /* parse with next DAI */ else { soc_dai_auto_select_format(available_fmt, rtd, idx + 1, best_fmt); } } } static unsigned int soc_dai_convert_possiblefmt_to_daifmt(u64 possible_fmt, unsigned int configured_fmt) { unsigned int fmt = 0; unsigned int mask = 0; /* * convert POSSIBLE_DAIFMT to DAIFMT * * Some basic/default settings on each is defined as 0. * see * SND_SOC_DAIFMT_NB_NF * SND_SOC_DAIFMT_GATED * * SND_SOC_DAIFMT_xxx_MASK can't notice it if Sound Card specify * these value, and will be overwrite to auto selected value. * * To avoid such issue, loop from 63 to 0 here. * Small number of SND_SOC_POSSIBLE_xxx will be Hi priority. * Basic/Default settings of each part and above are defined * as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx. */ for (int i = 63; i >= 0; i--) { u64 pos = 1ULL << i; switch (possible_fmt & pos) { /* * for format */ case SND_SOC_POSSIBLE_DAIFMT_I2S: case SND_SOC_POSSIBLE_DAIFMT_RIGHT_J: case SND_SOC_POSSIBLE_DAIFMT_LEFT_J: case SND_SOC_POSSIBLE_DAIFMT_DSP_A: case SND_SOC_POSSIBLE_DAIFMT_DSP_B: case SND_SOC_POSSIBLE_DAIFMT_AC97: case SND_SOC_POSSIBLE_DAIFMT_PDM: fmt = (fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | i; break; /* * for clock */ case SND_SOC_POSSIBLE_DAIFMT_CONT: fmt = (fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_CONT; break; case SND_SOC_POSSIBLE_DAIFMT_GATED: fmt = (fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_GATED; break; /* * for clock invert */ case SND_SOC_POSSIBLE_DAIFMT_NB_NF: fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_NF; break; case SND_SOC_POSSIBLE_DAIFMT_NB_IF: fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_IF; break; case SND_SOC_POSSIBLE_DAIFMT_IB_NF: fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_NF; break; case SND_SOC_POSSIBLE_DAIFMT_IB_IF: fmt = (fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_IF; break; } } /* * Some driver might have very complex limitation. * In such case, user want to auto-select non-limitation part, * and want to manually specify complex part. * * Or for example, if both CPU and Codec can be clock provider, * but because of its quality, user want to specify it manually. * * Ignore already configured format if exist */ if (!(configured_fmt & SND_SOC_DAIFMT_FORMAT_MASK)) mask |= SND_SOC_DAIFMT_FORMAT_MASK; if (!(configured_fmt & SND_SOC_DAIFMT_CLOCK_MASK)) mask |= SND_SOC_DAIFMT_CLOCK_MASK; if (!(configured_fmt & SND_SOC_DAIFMT_INV_MASK)) mask |= SND_SOC_DAIFMT_INV_MASK; if (!(configured_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK)) mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; return configured_fmt | (fmt & mask); } unsigned int snd_soc_dai_auto_select_format(const struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai_link *dai_link = rtd->dai_link; u64 possible_fmt = 0; soc_dai_auto_select_format(~0, rtd, 0, &possible_fmt); return soc_dai_convert_possiblefmt_to_daifmt(possible_fmt, dai_link->dai_fmt); } /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI * @fmt: SND_SOC_DAIFMT_* format value. * * Configures the DAI hardware format and clocking. */ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int 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); /** * snd_soc_xlate_tdm_slot_mask - generate tx/rx slot mask. * @slots: Number of slots in use. * @tx_mask: bitmask representing active TX slots. * @rx_mask: bitmask representing active RX slots. * * Generates the TDM tx and rx slot default masks for DAI. */ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, unsigned int *tx_mask, unsigned int *rx_mask) { if (*tx_mask || *rx_mask) return 0; if (!slots) return -EINVAL; *tx_mask = (1 << slots) - 1; *rx_mask = (1 << slots) - 1; return 0; } /** * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation * @dai: The DAI to configure * @tx_mask: bitmask representing active TX slots. * @rx_mask: bitmask representing active RX slots. * @slots: Number of slots in use. * @slot_width: Width in bits for each slot. * * This function configures the specified DAI for TDM operation. @slot contains * the total number of slots of the TDM stream and @slot_with the width of each * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the * active slots of the TDM stream for the specified DAI, i.e. which slots the * DAI should write to or read from. If a bit is set the corresponding slot is * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to * the first slot, bit 1 to the second slot and so on. The first active slot * maps to the first channel of the DAI, the second active slot to the second * channel and so on. * * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, * @rx_mask and @slot_width will be ignored. * * Returns 0 on success, a negative error code otherwise. */ 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) { int ret = -ENOTSUPP; int stream; unsigned int *tdm_mask[] = { &tx_mask, &rx_mask, }; if (slots) { if (dai->driver->ops && dai->driver->ops->xlate_tdm_slot_mask) ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); else ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); if (ret) goto err; } for_each_pcm_streams(stream) snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]); 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); err: return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); /** * snd_soc_dai_set_tdm_idle() - Configure a DAI's TDM idle mode * @dai: The DAI to configure * @tx_mask: bitmask representing idle TX slots. * @rx_mask: bitmask representing idle RX slots. * @tx_mode: idle mode to set for TX slots. * @rx_mode: idle mode to set for RX slots. * * This function configures the DAI to handle idle TDM slots in the * specified manner. @tx_mode and @rx_mode can be one of * SND_SOC_DAI_TDM_IDLE_NONE, SND_SOC_DAI_TDM_IDLE_ZERO, * SND_SOC_DAI_TDM_IDLE_PULLDOWN, or SND_SOC_DAI_TDM_IDLE_HIZ. * SND_SOC_TDM_IDLE_NONE represents the DAI's default/unset idle slot * handling state and could be any of the other modes depending on the * hardware behind the DAI. It is therefore undefined behaviour when set * explicitly. * * Mode and mask can be set independently for both the TX and RX direction. * Some hardware may ignore both TX and RX masks depending on its * capabilities. */ int snd_soc_dai_set_tdm_idle(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int tx_mode, int rx_mode) { int ret = -EOPNOTSUPP; /* You can't write to the RX line */ if (rx_mode == SND_SOC_DAI_TDM_IDLE_ZERO) return soc_dai_ret(dai, -EINVAL); if (dai->driver->ops && dai->driver->ops->set_tdm_idle) ret = dai->driver->ops->set_tdm_idle(dai, tx_mask, rx_mask, tx_mode, rx_mode); return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_idle); /** * snd_soc_dai_set_channel_map - configure DAI audio channel map * @dai: DAI * @tx_num: how many TX channels * @tx_slot: pointer to an array which imply the TX slot number channel * 0~num-1 uses * @rx_num: how many RX channels * @rx_slot: pointer to an array which imply the RX slot number channel * 0~num-1 uses * * configure the relationship between channel number and TDM slot number. */ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_slot, unsigned int rx_num, const unsigned int *rx_slot) { 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); /** * snd_soc_dai_get_channel_map - Get DAI audio channel map * @dai: DAI * @tx_num: how many TX channels * @tx_slot: pointer to an array which imply the TX slot number channel * 0~num-1 uses * @rx_num: how many RX channels * @rx_slot: pointer to an array which imply the RX slot number channel * 0~num-1 uses */ int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { 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); /** * snd_soc_dai_set_tristate - configure DAI system or master clock. * @dai: DAI * @tristate: tristate enable * * Tristates the DAI so that others can use it. */ int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) { 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); int snd_soc_dai_prepare(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { int ret = 0; if (!snd_soc_dai_stream_valid(dai, substream->stream)) return 0; if (dai->driver->ops && dai->driver->ops->prepare) ret = dai->driver->ops->prepare(substream, dai); return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_prepare); int snd_soc_dai_mute_is_ctrled_at_trigger(struct snd_soc_dai *dai) { if (dai->driver->ops) return dai->driver->ops->mute_unmute_on_trigger; return 0; } /** * snd_soc_dai_digital_mute - configure DAI system or master clock. * @dai: DAI * @mute: mute enable * @direction: stream to mute * * Mutes the DAI DAC. */ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, int direction) { int ret = -ENOTSUPP; /* * ignore if direction was CAPTURE * and it had .no_capture_mute flag */ if (dai->driver->ops && dai->driver->ops->mute_stream && (direction == SNDRV_PCM_STREAM_PLAYBACK || !dai->driver->ops->no_capture_mute)) ret = dai->driver->ops->mute_stream(dai, mute, direction); return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); int snd_soc_dai_hw_params(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { int ret = 0; if (dai->driver->ops && dai->driver->ops->hw_params) ret = dai->driver->ops->hw_params(substream, params, dai); /* mark substream if succeeded */ if (ret == 0) soc_dai_mark_push(dai, substream, hw_params); return soc_dai_ret(dai, ret); } void snd_soc_dai_hw_free(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int rollback) { if (rollback && !soc_dai_mark_match(dai, substream, hw_params)) return; if (dai->driver->ops && dai->driver->ops->hw_free) dai->driver->ops->hw_free(substream, dai); /* remove marked substream */ soc_dai_mark_pop(dai, hw_params); } int snd_soc_dai_startup(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { int ret = 0; if (!snd_soc_dai_stream_valid(dai, substream->stream)) return 0; if (dai->driver->ops && dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); /* mark substream if succeeded */ if (ret == 0) soc_dai_mark_push(dai, substream, startup); return soc_dai_ret(dai, ret); } void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int rollback) { if (!snd_soc_dai_stream_valid(dai, substream->stream)) return; if (rollback && !soc_dai_mark_match(dai, substream, startup)) return; if (dai->driver->ops && dai->driver->ops->shutdown) dai->driver->ops->shutdown(substream, dai); /* remove marked substream */ soc_dai_mark_pop(dai, startup); } int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd) { int ret = -ENOTSUPP; if (dai->driver->ops && dai->driver->ops->compress_new) ret = dai->driver->ops->compress_new(rtd); 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(const struct snd_soc_dai *dai, int dir) { const struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); /* If the codec specifies any channels at all, it supports the stream */ return stream->channels_min; } void snd_soc_dai_action(struct snd_soc_dai *dai, int stream, int action) { /* see snd_soc_dai_stream_active() */ dai->stream[stream].active += action; /* see snd_soc_component_active() */ dai->component->active += action; } EXPORT_SYMBOL_GPL(snd_soc_dai_action); int snd_soc_dai_active(const struct snd_soc_dai *dai) { int stream, active; active = 0; for_each_pcm_streams(stream) active += dai->stream[stream].active; 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->probed) continue; if (dai->driver->ops) { if (dai->driver->ops->probe_order != order) continue; if (dai->driver->ops->probe) { int ret = dai->driver->ops->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->probed) continue; if (dai->driver->ops) { if (dai->driver->ops->remove_order != order) continue; if (dai->driver->ops->remove) { r = dai->driver->ops->remove(dai); if (r < 0) ret = r; /* use last error */ } } dai->probed = 0; } return ret; } int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai; int i; for_each_rtd_dais(rtd, i, dai) { if (dai->driver->ops && dai->driver->ops->pcm_new) { int ret = dai->driver->ops->pcm_new(rtd, dai); if (ret < 0) return soc_dai_ret(dai, ret); } } return 0; } int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i, ret; for_each_rtd_dais(rtd, i, dai) { ret = snd_soc_dai_prepare(dai, substream); if (ret < 0) return ret; } return 0; } static int soc_dai_trigger(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int cmd) { int ret = 0; if (!snd_soc_dai_stream_valid(dai, substream->stream)) return 0; if (dai->driver->ops && dai->driver->ops->trigger) ret = dai->driver->ops->trigger(substream, cmd, dai); return soc_dai_ret(dai, ret); } int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, int cmd, int rollback) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i, r, ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: for_each_rtd_dais(rtd, i, dai) { ret = soc_dai_trigger(dai, substream, cmd); if (ret < 0) break; if (snd_soc_dai_mute_is_ctrled_at_trigger(dai)) snd_soc_dai_digital_mute(dai, 0, substream->stream); soc_dai_mark_push(dai, substream, trigger); } break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: for_each_rtd_dais(rtd, i, dai) { if (rollback && !soc_dai_mark_match(dai, substream, trigger)) continue; if (snd_soc_dai_mute_is_ctrled_at_trigger(dai)) snd_soc_dai_digital_mute(dai, 1, substream->stream); r = soc_dai_trigger(dai, substream, cmd); if (r < 0) ret = r; /* use last ret */ soc_dai_mark_pop(dai, trigger); } } return ret; } void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i; /* * We're looking for the delay through the full audio path so it needs to * be the maximum of the DAIs doing transmit and the maximum of the DAIs * doing receive (ie, all CPUs and all CODECs) rather than just the maximum * of all DAIs. */ /* for CPU */ for_each_rtd_cpu_dais(rtd, i, dai) if (dai->driver->ops && dai->driver->ops->delay) *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai)); /* for Codec */ for_each_rtd_codec_dais(rtd, i, dai) if (dai->driver->ops && dai->driver->ops->delay) *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai)); } int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, struct snd_compr_stream *cstream) { int ret = 0; if (dai->driver->cops && dai->driver->cops->startup) ret = dai->driver->cops->startup(cstream, dai); /* mark cstream if succeeded */ if (ret == 0) soc_dai_mark_push(dai, cstream, compr_startup); return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_compr_startup); void snd_soc_dai_compr_shutdown(struct snd_soc_dai *dai, struct snd_compr_stream *cstream, int rollback) { if (rollback && !soc_dai_mark_match(dai, cstream, compr_startup)) return; if (dai->driver->cops && dai->driver->cops->shutdown) dai->driver->cops->shutdown(cstream, dai); /* remove marked cstream */ soc_dai_mark_pop(dai, compr_startup); } EXPORT_SYMBOL_GPL(snd_soc_dai_compr_shutdown); 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_tstamp64 *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);
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1