| From 87f734fa8214b4ddbfdac9b7ac5dc75a3d86badb Mon Sep 17 00:00:00 2001 |
| From: Shengjiu Wang <shengjiu.wang@nxp.com> |
| Date: Tue, 23 Jan 2018 13:26:37 +0800 |
| Subject: [PATCH] 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> |
| --- |
| sound/soc/fsl/fsl_sai.c | 162 +++++++++++++++++++++++++++++++++++++++++++++--- |
| sound/soc/fsl/fsl_sai.h | 12 +++- |
| 2 files changed, 164 insertions(+), 10 deletions(-) |
| |
| --- a/sound/soc/fsl/fsl_sai.c |
| +++ b/sound/soc/fsl/fsl_sai.c |
| @@ -306,6 +306,11 @@ static int fsl_sai_set_dai_fmt_tr(struct |
| 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: |
| @@ -493,12 +498,38 @@ static int fsl_sai_hw_params(struct snd_ |
| 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; |
| |
| @@ -528,7 +559,7 @@ static int fsl_sai_hw_params(struct snd_ |
| 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); |
| @@ -561,17 +592,71 @@ static int fsl_sai_hw_params(struct snd_ |
| } |
| |
| 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, |
| @@ -611,9 +696,18 @@ static int fsl_sai_trigger(struct snd_pc |
| 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. |
| @@ -632,10 +726,19 @@ static int fsl_sai_trigger(struct snd_pc |
| 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); |
| @@ -995,6 +1098,7 @@ static int fsl_sai_probe(struct platform |
| 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; |
| |
| @@ -1049,6 +1153,9 @@ static int fsl_sai_probe(struct platform |
| } |
| } |
| |
| + 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) |
| @@ -1063,6 +1170,37 @@ static int fsl_sai_probe(struct platform |
| 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)) |
| { |
| @@ -1145,6 +1283,11 @@ static int fsl_sai_probe(struct platform |
| 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); |
| --- a/sound/soc/fsl/fsl_sai.h |
| +++ b/sound/soc/fsl/fsl_sai.h |
| @@ -11,7 +11,10 @@ |
| |
| #define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ |
| SNDRV_PCM_FMTBIT_S24_LE |\ |
| - SNDRV_PCM_FMTBIT_S32_LE) |
| + SNDRV_PCM_FMTBIT_S32_LE |\ |
| + SNDRV_PCM_FMTBIT_DSD_U8 |\ |
| + SNDRV_PCM_FMTBIT_DSD_U16_LE |\ |
| + SNDRV_PCM_FMTBIT_DSD_U32_LE) |
| |
| /* SAI Register Map Register */ |
| #define FSL_SAI_TCSR(offset) (0x00 + offset) /* SAI Transmit Control */ |
| @@ -172,9 +175,14 @@ struct fsl_sai { |
| bool slave_mode[2]; |
| bool is_lsb_first; |
| bool is_dsp_mode; |
| + bool is_multi_lane; |
| bool synchronous[2]; |
| bool is_stream_opened[2]; |
| + bool is_dsd; |
| unsigned int dataline[2]; |
| + unsigned int dataline_dsd[2]; |
| + unsigned int dataline_off[2]; |
| + unsigned int dataline_off_dsd[2]; |
| unsigned int masterflag[2]; |
| |
| unsigned int mclk_id[2]; |
| @@ -188,6 +196,8 @@ struct fsl_sai { |
| struct snd_dmaengine_dai_dma_data dma_params_tx; |
| const struct fsl_sai_soc_data *soc; |
| struct pm_qos_request pm_qos_req; |
| + struct pinctrl *pinctrl; |
| + struct pinctrl_state *pins_dsd; |
| }; |
| |
| #define TX 1 |