summaryrefslogtreecommitdiff
path: root/sound/soc/qcom/qdsp6/q6afe-dai.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/qcom/qdsp6/q6afe-dai.c')
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c76
1 files changed, 70 insertions, 6 deletions
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index a9c4f896a7df..0f47aadaabe1 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -92,6 +92,39 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int q6afe_usb_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int channels = params_channels(params);
+ int rate = params_rate(params);
+ struct q6afe_usb_cfg *usb = &dai_data->port_config[dai->id].usb_audio;
+
+ usb->sample_rate = rate;
+ usb->num_channels = channels;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ usb->bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ usb->bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ usb->bit_width = 32;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int q6i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -172,8 +205,8 @@ static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai,
}
static int q6tdm_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_slot,
- unsigned int rx_num, unsigned int *rx_slot)
+ unsigned int tx_num, const unsigned int *tx_slot,
+ unsigned int rx_num, const unsigned int *rx_slot)
{
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
@@ -250,8 +283,10 @@ static int q6tdm_hw_params(struct snd_pcm_substream *substream,
}
static int q6dma_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_ch_mask,
- unsigned int rx_num, unsigned int *rx_ch_mask)
+ unsigned int tx_num,
+ const unsigned int *tx_ch_mask,
+ unsigned int rx_num,
+ const unsigned int *rx_ch_mask)
{
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
@@ -392,6 +427,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream,
q6afe_cdc_dma_port_prepare(dai_data->port[dai->id],
&dai_data->port_config[dai->id].dma_cfg);
break;
+ case USB_RX:
+ q6afe_usb_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].usb_audio);
+ break;
default:
return -EINVAL;
}
@@ -407,8 +446,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream,
}
static int q6slim_set_channel_map(struct snd_soc_dai *dai,
- unsigned int tx_num, unsigned int *tx_slot,
- unsigned int rx_num, unsigned int *rx_slot)
+ unsigned int tx_num,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const unsigned int *rx_slot)
{
struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id];
@@ -618,6 +659,9 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = {
{"TX_CODEC_DMA_TX_5", NULL, "TX_CODEC_DMA_TX_5 Capture"},
{"RX_CODEC_DMA_RX_6 Playback", NULL, "RX_CODEC_DMA_RX_6"},
{"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"},
+
+ /* USB playback AFE port receives data for playback, hence use the RX port */
+ {"USB Playback", NULL, "USB_RX"},
};
static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
@@ -645,6 +689,23 @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops q6afe_usb_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .prepare = q6afe_dai_prepare,
+ .hw_params = q6afe_usb_hw_params,
+ /*
+ * Shutdown callback required to stop the USB AFE port, which is enabled
+ * by the prepare() stage. This stops the audio traffic on the USB AFE
+ * port on the Q6DSP.
+ */
+ .shutdown = q6afe_dai_shutdown,
+ /*
+ * Startup callback not needed, as AFE port start command passes the PCM
+ * parameters within the AFE command, which is provided by the PCM core
+ * during the prepare() stage.
+ */
+};
+
static const struct snd_soc_dai_ops q6hdmi_ops = {
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
@@ -943,6 +1004,8 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = {
0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_7", "NULL",
0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_component_driver q6afe_dai_component = {
@@ -1057,6 +1120,7 @@ static int q6afe_dai_dev_probe(struct platform_device *pdev)
cfg.q6i2s_ops = &q6i2s_ops;
cfg.q6tdm_ops = &q6tdm_ops;
cfg.q6dma_ops = &q6dma_ops;
+ cfg.q6usb_ops = &q6afe_usb_ops;
dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais);