diff options
Diffstat (limited to 'sound/soc/fsl/fsl_sai.c')
-rw-r--r-- | sound/soc/fsl/fsl_sai.c | 162 |
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); |