summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_sai.c
diff options
context:
space:
mode:
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);