diff options
| author | Zidan Wang <zidan.wang@freescale.com> | 2015-05-11 06:24:41 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2015-05-12 14:43:51 -0400 |
| commit | c3ecef21c3f26bf4737fc0887964127accfa8a0e (patch) | |
| tree | ab1286cbed64158c8ab405885bd0c5bf444ab953 | |
| parent | b787f68c36d49bb1d9236f403813641efa74a031 (diff) | |
ASoC: fsl_sai: add sai master mode support
When sai works on master mode, set its bit clock and frame clock.
SAI has 4 MCLK source, bus clock, MCLK1, MCLK2 and MCLK3. fsl_sai_set_bclk
will select proper MCLK source, then calculate and set the bit clock divider.
After fsl_sai_set_bclk, enable the selected mclk in hw_params(), and add
hw_free() to disable the mclk.
Signed-off-by: Zidan Wang <zidan.wang@freescale.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | sound/soc/fsl/fsl_sai.c | 117 | ||||
| -rw-r--r-- | sound/soc/fsl/fsl_sai.h | 9 |
2 files changed, 121 insertions, 5 deletions
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index ec79c3d5e65e..cca72b8287a9 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Freescale ALSA SoC Digital Audio Interface (SAI) driver. | 2 | * Freescale ALSA SoC Digital Audio Interface (SAI) driver. |
| 3 | * | 3 | * |
| 4 | * Copyright 2012-2013 Freescale Semiconductor, Inc. | 4 | * Copyright 2012-2015 Freescale Semiconductor, Inc. |
| 5 | * | 5 | * |
| 6 | * This program is free software, you can redistribute it and/or modify it | 6 | * This program is free software, you can redistribute it and/or modify it |
| 7 | * under the terms of the GNU General Public License as published by the | 7 | * under the terms of the GNU General Public License as published by the |
| @@ -251,12 +251,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, | |||
| 251 | val_cr4 |= FSL_SAI_CR4_FSD_MSTR; | 251 | val_cr4 |= FSL_SAI_CR4_FSD_MSTR; |
| 252 | break; | 252 | break; |
| 253 | case SND_SOC_DAIFMT_CBM_CFM: | 253 | case SND_SOC_DAIFMT_CBM_CFM: |
| 254 | sai->is_slave_mode = true; | ||
| 254 | break; | 255 | break; |
| 255 | case SND_SOC_DAIFMT_CBS_CFM: | 256 | case SND_SOC_DAIFMT_CBS_CFM: |
| 256 | val_cr2 |= FSL_SAI_CR2_BCD_MSTR; | 257 | val_cr2 |= FSL_SAI_CR2_BCD_MSTR; |
| 257 | break; | 258 | break; |
| 258 | case SND_SOC_DAIFMT_CBM_CFS: | 259 | case SND_SOC_DAIFMT_CBM_CFS: |
| 259 | val_cr4 |= FSL_SAI_CR4_FSD_MSTR; | 260 | val_cr4 |= FSL_SAI_CR4_FSD_MSTR; |
| 261 | sai->is_slave_mode = true; | ||
| 260 | break; | 262 | break; |
| 261 | default: | 263 | default: |
| 262 | return -EINVAL; | 264 | return -EINVAL; |
| @@ -288,6 +290,79 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |||
| 288 | return ret; | 290 | return ret; |
| 289 | } | 291 | } |
| 290 | 292 | ||
| 293 | static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) | ||
| 294 | { | ||
| 295 | struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); | ||
| 296 | unsigned long clk_rate; | ||
| 297 | u32 savediv = 0, ratio, savesub = freq; | ||
| 298 | u32 id; | ||
| 299 | int ret = 0; | ||
| 300 | |||
| 301 | /* Don't apply to slave mode */ | ||
| 302 | if (sai->is_slave_mode) | ||
| 303 | return 0; | ||
| 304 | |||
| 305 | for (id = 0; id < FSL_SAI_MCLK_MAX; id++) { | ||
| 306 | clk_rate = clk_get_rate(sai->mclk_clk[id]); | ||
| 307 | if (!clk_rate) | ||
| 308 | continue; | ||
| 309 | |||
| 310 | ratio = clk_rate / freq; | ||
| 311 | |||
| 312 | ret = clk_rate - ratio * freq; | ||
| 313 | |||
| 314 | /* | ||
| 315 | * Drop the source that can not be | ||
| 316 | * divided into the required rate. | ||
| 317 | */ | ||
| 318 | if (ret != 0 && clk_rate / ret < 1000) | ||
| 319 | continue; | ||
| 320 | |||
| 321 | dev_dbg(dai->dev, | ||
| 322 | "ratio %d for freq %dHz based on clock %ldHz\n", | ||
| 323 | ratio, freq, clk_rate); | ||
| 324 | |||
| 325 | if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512) | ||
| 326 | ratio /= 2; | ||
| 327 | else | ||
| 328 | continue; | ||
| 329 | |||
| 330 | if (ret < savesub) { | ||
| 331 | savediv = ratio; | ||
| 332 | sai->mclk_id[tx] = id; | ||
| 333 | savesub = ret; | ||
| 334 | } | ||
| 335 | |||
| 336 | if (ret == 0) | ||
| 337 | break; | ||
| 338 | } | ||
| 339 | |||
| 340 | if (savediv == 0) { | ||
| 341 | dev_err(dai->dev, "failed to derive required %cx rate: %d\n", | ||
| 342 | tx ? 'T' : 'R', freq); | ||
| 343 | return -EINVAL; | ||
| 344 | } | ||
| 345 | |||
| 346 | if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) { | ||
| 347 | regmap_update_bits(sai->regmap, FSL_SAI_RCR2, | ||
| 348 | FSL_SAI_CR2_MSEL_MASK, | ||
| 349 | FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); | ||
| 350 | regmap_update_bits(sai->regmap, FSL_SAI_RCR2, | ||
| 351 | FSL_SAI_CR2_DIV_MASK, savediv - 1); | ||
| 352 | } else { | ||
| 353 | regmap_update_bits(sai->regmap, FSL_SAI_TCR2, | ||
| 354 | FSL_SAI_CR2_MSEL_MASK, | ||
| 355 | FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); | ||
| 356 | regmap_update_bits(sai->regmap, FSL_SAI_TCR2, | ||
| 357 | FSL_SAI_CR2_DIV_MASK, savediv - 1); | ||
| 358 | } | ||
| 359 | |||
| 360 | dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", | ||
| 361 | sai->mclk_id[tx], savediv, savesub); | ||
| 362 | |||
| 363 | return 0; | ||
| 364 | } | ||
| 365 | |||
| 291 | static int fsl_sai_hw_params(struct snd_pcm_substream *substream, | 366 | static int fsl_sai_hw_params(struct snd_pcm_substream *substream, |
| 292 | struct snd_pcm_hw_params *params, | 367 | struct snd_pcm_hw_params *params, |
| 293 | struct snd_soc_dai *cpu_dai) | 368 | struct snd_soc_dai *cpu_dai) |
| @@ -297,6 +372,24 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, | |||
| 297 | unsigned int channels = params_channels(params); | 372 | unsigned int channels = params_channels(params); |
| 298 | u32 word_width = snd_pcm_format_width(params_format(params)); | 373 | u32 word_width = snd_pcm_format_width(params_format(params)); |
| 299 | u32 val_cr4 = 0, val_cr5 = 0; | 374 | u32 val_cr4 = 0, val_cr5 = 0; |
| 375 | int ret; | ||
| 376 | |||
| 377 | if (!sai->is_slave_mode) { | ||
| 378 | ret = fsl_sai_set_bclk(cpu_dai, tx, | ||
| 379 | 2 * word_width * params_rate(params)); | ||
| 380 | if (ret) | ||
| 381 | return ret; | ||
| 382 | |||
| 383 | /* Do not enable the clock if it is already enabled */ | ||
| 384 | if (!(sai->mclk_streams & BIT(substream->stream))) { | ||
| 385 | ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]); | ||
| 386 | if (ret) | ||
| 387 | return ret; | ||
| 388 | |||
| 389 | sai->mclk_streams |= BIT(substream->stream); | ||
| 390 | } | ||
| 391 | |||
| 392 | } | ||
| 300 | 393 | ||
| 301 | if (!sai->is_dsp_mode) | 394 | if (!sai->is_dsp_mode) |
| 302 | val_cr4 |= FSL_SAI_CR4_SYWD(word_width); | 395 | val_cr4 |= FSL_SAI_CR4_SYWD(word_width); |
| @@ -322,6 +415,22 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, | |||
| 322 | return 0; | 415 | return 0; |
| 323 | } | 416 | } |
| 324 | 417 | ||
| 418 | static int fsl_sai_hw_free(struct snd_pcm_substream *substream, | ||
| 419 | struct snd_soc_dai *cpu_dai) | ||
| 420 | { | ||
| 421 | struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); | ||
| 422 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
| 423 | |||
| 424 | if (!sai->is_slave_mode && | ||
| 425 | sai->mclk_streams & BIT(substream->stream)) { | ||
| 426 | clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); | ||
| 427 | sai->mclk_streams &= ~BIT(substream->stream); | ||
| 428 | } | ||
| 429 | |||
| 430 | return 0; | ||
| 431 | } | ||
| 432 | |||
| 433 | |||
| 325 | static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, | 434 | static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, |
| 326 | struct snd_soc_dai *cpu_dai) | 435 | struct snd_soc_dai *cpu_dai) |
| 327 | { | 436 | { |
| @@ -428,6 +537,7 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { | |||
| 428 | .set_sysclk = fsl_sai_set_dai_sysclk, | 537 | .set_sysclk = fsl_sai_set_dai_sysclk, |
| 429 | .set_fmt = fsl_sai_set_dai_fmt, | 538 | .set_fmt = fsl_sai_set_dai_fmt, |
| 430 | .hw_params = fsl_sai_hw_params, | 539 | .hw_params = fsl_sai_hw_params, |
| 540 | .hw_free = fsl_sai_hw_free, | ||
| 431 | .trigger = fsl_sai_trigger, | 541 | .trigger = fsl_sai_trigger, |
| 432 | .startup = fsl_sai_startup, | 542 | .startup = fsl_sai_startup, |
| 433 | .shutdown = fsl_sai_shutdown, | 543 | .shutdown = fsl_sai_shutdown, |
| @@ -600,8 +710,9 @@ static int fsl_sai_probe(struct platform_device *pdev) | |||
| 600 | sai->bus_clk = NULL; | 710 | sai->bus_clk = NULL; |
| 601 | } | 711 | } |
| 602 | 712 | ||
| 603 | for (i = 0; i < FSL_SAI_MCLK_MAX; i++) { | 713 | sai->mclk_clk[0] = sai->bus_clk; |
| 604 | sprintf(tmp, "mclk%d", i + 1); | 714 | for (i = 1; i < FSL_SAI_MCLK_MAX; i++) { |
| 715 | sprintf(tmp, "mclk%d", i); | ||
| 605 | sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); | 716 | sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); |
| 606 | if (IS_ERR(sai->mclk_clk[i])) { | 717 | if (IS_ERR(sai->mclk_clk[i])) { |
| 607 | dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n", | 718 | dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n", |
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 34667209b607..066280953c85 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h | |||
| @@ -72,13 +72,15 @@ | |||
| 72 | 72 | ||
| 73 | /* SAI Transmit and Recieve Configuration 2 Register */ | 73 | /* SAI Transmit and Recieve Configuration 2 Register */ |
| 74 | #define FSL_SAI_CR2_SYNC BIT(30) | 74 | #define FSL_SAI_CR2_SYNC BIT(30) |
| 75 | #define FSL_SAI_CR2_MSEL_MASK (0xff << 26) | 75 | #define FSL_SAI_CR2_MSEL_MASK (0x3 << 26) |
| 76 | #define FSL_SAI_CR2_MSEL_BUS 0 | 76 | #define FSL_SAI_CR2_MSEL_BUS 0 |
| 77 | #define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) | 77 | #define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) |
| 78 | #define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) | 78 | #define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) |
| 79 | #define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) | 79 | #define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) |
| 80 | #define FSL_SAI_CR2_MSEL(ID) ((ID) << 26) | ||
| 80 | #define FSL_SAI_CR2_BCP BIT(25) | 81 | #define FSL_SAI_CR2_BCP BIT(25) |
| 81 | #define FSL_SAI_CR2_BCD_MSTR BIT(24) | 82 | #define FSL_SAI_CR2_BCD_MSTR BIT(24) |
| 83 | #define FSL_SAI_CR2_DIV_MASK 0xff | ||
| 82 | 84 | ||
| 83 | /* SAI Transmit and Recieve Configuration 3 Register */ | 85 | /* SAI Transmit and Recieve Configuration 3 Register */ |
| 84 | #define FSL_SAI_CR3_TRCE BIT(16) | 86 | #define FSL_SAI_CR3_TRCE BIT(16) |
| @@ -120,7 +122,7 @@ | |||
| 120 | #define FSL_SAI_CLK_MAST2 2 | 122 | #define FSL_SAI_CLK_MAST2 2 |
| 121 | #define FSL_SAI_CLK_MAST3 3 | 123 | #define FSL_SAI_CLK_MAST3 3 |
| 122 | 124 | ||
| 123 | #define FSL_SAI_MCLK_MAX 3 | 125 | #define FSL_SAI_MCLK_MAX 4 |
| 124 | 126 | ||
| 125 | /* SAI data transfer numbers per DMA request */ | 127 | /* SAI data transfer numbers per DMA request */ |
| 126 | #define FSL_SAI_MAXBURST_TX 6 | 128 | #define FSL_SAI_MAXBURST_TX 6 |
| @@ -132,11 +134,14 @@ struct fsl_sai { | |||
| 132 | struct clk *bus_clk; | 134 | struct clk *bus_clk; |
| 133 | struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; | 135 | struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; |
| 134 | 136 | ||
| 137 | bool is_slave_mode; | ||
| 135 | bool is_lsb_first; | 138 | bool is_lsb_first; |
| 136 | bool is_dsp_mode; | 139 | bool is_dsp_mode; |
| 137 | bool sai_on_imx; | 140 | bool sai_on_imx; |
| 138 | bool synchronous[2]; | 141 | bool synchronous[2]; |
| 139 | 142 | ||
| 143 | unsigned int mclk_id[2]; | ||
| 144 | unsigned int mclk_streams; | ||
| 140 | struct snd_dmaengine_dai_dma_data dma_params_rx; | 145 | struct snd_dmaengine_dai_dma_data dma_params_rx; |
| 141 | struct snd_dmaengine_dai_dma_data dma_params_tx; | 146 | struct snd_dmaengine_dai_dma_data dma_params_tx; |
| 142 | }; | 147 | }; |
