/* * 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, unsignedint 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);
/** * 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);
/** * 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, unsignedint freq_in, unsignedint 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);
/** * 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, unsignedint ratio)
{ int ret = -ENOTSUPP;
if (dai->driver->ops &&
dai->driver->ops->set_bclk_ratio)
ret = dai->driver->ops->set_bclk_ratio(dai, ratio);
int snd_soc_dai_get_fmt_max_priority(conststruct snd_soc_pcm_runtime *rtd)
{ struct snd_soc_dai *dai; int i, max = 0;
/* * return max num if *ALL* DAIs have .auto_selectable_formats
*/
for_each_rtd_dais(rtd, i, dai) { if (dai->driver->ops &&
dai->driver->ops->num_auto_selectable_formats)
max = max(max, dai->driver->ops->num_auto_selectable_formats); else return 0;
}
return max;
}
/** * snd_soc_dai_get_fmt - get supported audio format. * @dai: DAI * @priority: priority level of supported audio format. * * This should return only formats implemented with high * quality by the DAI so that the core can configure a * format which will work well with other devices. * For example devices which don't support both edges of the * LRCLK signal in I2S style formats should only list DSP * modes. This will mean that sometimes fewer formats * are reported here than are supported by set_fmt().
*/
u64 snd_soc_dai_get_fmt(conststruct snd_soc_dai *dai, int priority)
{ conststruct snd_soc_dai_ops *ops = dai->driver->ops;
u64 fmt = 0; int i, max = 0, until = priority;
/* * Collect auto_selectable_formats until priority * * ex) * auto_selectable_formats[] = { A, B, C }; * (A, B, C = SND_SOC_POSSIBLE_DAIFMT_xxx) * * priority = 1 : A * priority = 2 : A | B * priority = 3 : A | B | C * priority = 4 : A | B | C * ...
*/ if (ops)
max = ops->num_auto_selectable_formats;
if (max < until)
until = max;
for (i = 0; i < until; i++)
fmt |= ops->auto_selectable_formats[i];
return 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, unsignedint fmt)
{ int ret = -ENOTSUPP;
if (dai->driver->ops && dai->driver->ops->set_fmt)
ret = dai->driver->ops->set_fmt(dai, fmt);
/** * 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, unsignedint tx_mask, unsignedint rx_mask, int slots, int slot_width)
{ int ret = -ENOTSUPP; int stream; unsignedint *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;
}
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_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, unsignedint tx_num, constunsignedint *tx_slot, unsignedint rx_num, constunsignedint *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(conststruct snd_soc_dai *dai, unsignedint *tx_num, unsignedint *tx_slot, unsignedint *rx_num, unsignedint *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);
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);
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(conststruct snd_soc_dai *dai, int dir)
{ conststruct 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(conststruct snd_soc_dai *dai)
{ int stream, active;
active = 0;
for_each_pcm_streams(stream)
active += dai->stream[stream].active;
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;
}
staticint 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);
}
}
/* * 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.