summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_sai.c
diff options
context:
space:
mode:
authorShengjiu Wang <shengjiu.wang@nxp.com>2018-01-23 13:26:37 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2019-11-25 15:53:50 +0800
commit15e50be3635bb61fdefc65327efc3f378d3ffb60 (patch)
treef21cf5b806868e513a437e484a73df95915add4d /sound/soc/fsl/fsl_sai.c
parent82a7f067a1461350596569f61cfa313947228159 (diff)
MLK-16224-4: ASoC: fsl_sai: support multi fifo and DSD
The codec always mux the LRCLK pin to DSD data line, so when we want to support DSD, the pinmux is different. For two channel DSD, the DSDL is mapped to TX0, but the DSDR is mapped to TX4, there is address offset for the fifo address of TX0 and TX4, TX4's fifo is not adjacent to TX0's. Usually, if mapping is TX0 and TX1, that will be easy for SAI and SDMA to handle, that SAI can use the FIFO combine mode, SDMA can use the normal script. so for DSD: 1. The SDMA should use the multi-fifo script, and SAI can't use the FIFO combine mode. 2. driver should to check the dts configuration(fsl,dataline) for which dataline is used corrently 3. maxburst is the multiply of datalines 4. each channel of DSD occupy one data lane 5. according to data lane, set TRCE bits Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Reviewed-by: Viorel Suman <viorel.suman@nxp.com>
Diffstat (limited to 'sound/soc/fsl/fsl_sai.c')
-rw-r--r--sound/soc/fsl/fsl_sai.c162
1 files changed, 153 insertions, 9 deletions
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 3e7cd709ac1e..4f49e674b486 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -267,6 +267,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
if (!sai->is_lsb_first)
val_cr4 |= FSL_SAI_CR4_MF;
+ sai->is_dsp_mode = false;
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -305,6 +306,11 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
val_cr2 |= FSL_SAI_CR2_BCP;
sai->is_dsp_mode = true;
break;
+ case SND_SOC_DAIFMT_PDM:
+ val_cr2 |= FSL_SAI_CR2_BCP;
+ val_cr4 &= ~FSL_SAI_CR4_MF;
+ sai->is_dsp_mode = true;
+ break;
case SND_SOC_DAIFMT_RIGHT_J:
/* To be done */
default:
@@ -492,12 +498,38 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
u32 slot_width = word_width;
u32 pins;
int ret;
+ int i;
+ int trce_mask = 0;
+ snd_pcm_format_t format = params_format(params);
if (sai->slots)
slots = sai->slots;
pins = DIV_ROUND_UP(channels, slots);
+ if (format == SNDRV_PCM_FORMAT_DSD_U8 ||
+ format == SNDRV_PCM_FORMAT_DSD_U16_LE ||
+ format == SNDRV_PCM_FORMAT_DSD_U16_BE ||
+ format == SNDRV_PCM_FORMAT_DSD_U32_LE ||
+ format == SNDRV_PCM_FORMAT_DSD_U32_BE) {
+ sai->is_dsd = true;
+
+ if (!IS_ERR_OR_NULL(sai->pins_dsd)) {
+ ret = pinctrl_select_state(sai->pinctrl, sai->pins_dsd);
+ if (ret) {
+ dev_err(cpu_dai->dev,
+ "failed to set proper pins state: %d\n", ret);
+ return ret;
+ }
+ }
+ } else {
+ pinctrl_pm_select_default_state(cpu_dai->dev);
+ sai->is_dsd = false;
+ }
+
+ if (sai->is_dsd)
+ pins = channels;
+
if (sai->slot_width)
slot_width = sai->slot_width;
@@ -527,7 +559,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
- if (sai->is_lsb_first)
+ if (sai->is_lsb_first || sai->is_dsd)
val_cr5 |= FSL_SAI_CR5_FBT(0);
else
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
@@ -560,17 +592,71 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
}
if (sai->soc->dataline != 0x1) {
- if (sai->dataline[tx] <= 1)
+
+ if (sai->dataline[tx] <= 1 || sai->is_multi_lane)
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, offset),
FSL_SAI_CR4_FCOMB_MASK, 0);
else
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, offset),
FSL_SAI_CR4_FCOMB_MASK, FSL_SAI_CR4_FCOMB_SOFT);
+
+ if (sai->is_multi_lane) {
+ if (tx) {
+ sai->dma_params_tx.maxburst =
+ FSL_SAI_MAXBURST_TX * pins;
+ if (sai->is_dsd)
+ sai->dma_params_tx.fifo_num = pins +
+ (sai->dataline_off_dsd[tx] << 8);
+ else
+ sai->dma_params_tx.fifo_num = pins +
+ (sai->dataline_off[tx] << 8);
+ } else {
+ sai->dma_params_rx.maxburst =
+ FSL_SAI_MAXBURST_RX * pins;
+ if (sai->is_dsd)
+ sai->dma_params_rx.fifo_num = pins +
+ (sai->dataline_off_dsd[tx] << 8);
+ else
+ sai->dma_params_rx.fifo_num = pins +
+ (sai->dataline_off[tx] << 8);
+ }
+ }
+
+ snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
+ &sai->dma_params_rx);
}
- regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, offset),
+ if (sai->is_dsd) {
+ if (__sw_hweight8(sai->dataline_dsd[tx] & 0xFF) < pins) {
+ dev_err(cpu_dai->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+ /*find a proper tcre setting*/
+ for (i = 0; i < 8; i++) {
+ trce_mask = (1 << (i + 1)) - 1;
+ if (__sw_hweight8(sai->dataline_dsd[tx] & trce_mask) == pins)
+ break;
+ }
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, offset),
FSL_SAI_CR3_TRCE_MASK,
- FSL_SAI_CR3_TRCE((sai->dataline[tx] & ((1 << pins) - 1))));
+ FSL_SAI_CR3_TRCE((sai->dataline_dsd[tx] & trce_mask)));
+ } else {
+ if (__sw_hweight8(sai->dataline[tx] & 0xFF) < pins) {
+ dev_err(cpu_dai->dev, "channel not supported\n");
+ return -EINVAL;
+ }
+ /*find a proper tcre setting*/
+ for (i = 0; i < 8; i++) {
+ trce_mask = (1 << (i + 1)) - 1;
+ if (__sw_hweight8(sai->dataline[tx] & trce_mask) == pins)
+ break;
+ }
+
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, offset),
+ FSL_SAI_CR3_TRCE_MASK,
+ FSL_SAI_CR3_TRCE((sai->dataline[tx] & trce_mask)));
+ }
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, offset),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
@@ -610,9 +696,18 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
unsigned char offset = sai->soc->reg_offset;
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u8 channels = substream->runtime->channels;
+ u32 slots = (channels == 1) ? 2 : channels;
u32 xcsr, count = 100;
- int i;
+ u32 pins;
+ int i = 0, j = 0, k = 0;
+
+ if (sai->slots)
+ slots = sai->slots;
+
+ pins = DIV_ROUND_UP(channels, slots);
+ if (sai->is_dsd)
+ pins = channels;
/*
* Asynchronous mode: Clear SYNC for both Tx and Rx.
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
@@ -631,10 +726,19 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- for (i = 0; tx && i < channels; i++)
- regmap_write(sai->regmap, FSL_SAI_TDR0, 0x0);
- if (tx)
- udelay(10);
+
+ for (i = 0; tx && i < channels; i++) {
+ while (tx && i < channels)
+ if (sai->dataline[tx] & (1 << j)) {
+ regmap_write(sai->regmap, FSL_SAI_TDR0 + j * 0x4, 0x0);
+ i++;
+ k++;
+ }
+ j++;
+
+ if (k%pins == 0)
+ j = 0;
+ }
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, offset),
FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
@@ -994,6 +1098,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
char tmp[8];
int irq, ret, i;
int index;
+ int firstbitidx, nextbitidx;
struct regmap_config fsl_sai_regmap_config = fsl_sai_v2_regmap_config;
unsigned long irqflags = 0;
@@ -1048,6 +1153,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
}
}
+ if (of_find_property(np, "fsl,sai-multi-lane", NULL))
+ sai->is_multi_lane = true;
+
/*dataline mask for rx and tx*/
ret = of_property_read_u32_index(np, "fsl,dataline", 0, &sai->dataline[0]);
if (ret)
@@ -1062,6 +1170,37 @@ static int fsl_sai_probe(struct platform_device *pdev)
return -EINVAL;
}
+ for (i = 0; i < 2; i++) {
+ firstbitidx = find_first_bit((const unsigned long *)&sai->dataline[i], 8);
+ nextbitidx = find_next_bit((const unsigned long *)&sai->dataline[i], 8, firstbitidx+1);
+ sai->dataline_off[i] = nextbitidx - firstbitidx - 1;
+
+ if (sai->dataline_off[i] < 0 || sai->dataline_off[i] >= 7)
+ sai->dataline_off[i] = 0;
+ }
+
+ ret = of_property_read_u32_index(np, "fsl,dataline,dsd", 0, &sai->dataline_dsd[0]);
+ if (ret)
+ sai->dataline_dsd[0] = 1;
+
+ ret = of_property_read_u32_index(np, "fsl,dataline,dsd", 1, &sai->dataline_dsd[1]);
+ if (ret)
+ sai->dataline_dsd[1] = 1;
+
+ if ((sai->dataline_dsd[0] & (~sai->soc->dataline)) || sai->dataline_dsd[1] & (~sai->soc->dataline)) {
+ dev_err(&pdev->dev, "dataline setting error, Mask is 0x%x\n", sai->soc->dataline);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ firstbitidx = find_first_bit((const unsigned long *)&sai->dataline_dsd[i], 8);
+ nextbitidx = find_next_bit((const unsigned long *)&sai->dataline_dsd[i], 8, firstbitidx+1);
+ sai->dataline_off_dsd[i] = nextbitidx - firstbitidx - 1;
+
+ if (sai->dataline_off_dsd[i] < 0 || sai->dataline_off_dsd[i] >= 7)
+ sai->dataline_off_dsd[i] = 0;
+ }
+
if ((of_find_property(np, "fsl,i2s-xtor", NULL) != NULL) ||
(of_find_property(np, "fsl,txm-rxs", NULL) != NULL))
{
@@ -1143,6 +1282,11 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
+ sai->pinctrl = devm_pinctrl_get(&pdev->dev);
+
+ if (!IS_ERR_OR_NULL(sai->pinctrl))
+ sai->pins_dsd = pinctrl_lookup_state(sai->pinctrl, "dsd");
+
platform_set_drvdata(pdev, sai);
pm_runtime_enable(&pdev->dev);