diff options
| -rw-r--r-- | sound/soc/atmel/atmel_ssc_dai.c | 111 | ||||
| -rw-r--r-- | sound/soc/atmel/atmel_ssc_dai.h | 1 |
2 files changed, 108 insertions, 4 deletions
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 379ac2a6ab16..6b8e64899205 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c | |||
| @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) | |||
| 187 | return IRQ_HANDLED; | 187 | return IRQ_HANDLED; |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | /* | ||
| 191 | * When the bit clock is input, limit the maximum rate according to the | ||
| 192 | * Serial Clock Ratio Considerations section from the SSC documentation: | ||
| 193 | * | ||
| 194 | * The Transmitter and the Receiver can be programmed to operate | ||
| 195 | * with the clock signals provided on either the TK or RK pins. | ||
| 196 | * This allows the SSC to support many slave-mode data transfers. | ||
| 197 | * In this case, the maximum clock speed allowed on the RK pin is: | ||
| 198 | * - Peripheral clock divided by 2 if Receiver Frame Synchro is input | ||
| 199 | * - Peripheral clock divided by 3 if Receiver Frame Synchro is output | ||
| 200 | * In addition, the maximum clock speed allowed on the TK pin is: | ||
| 201 | * - Peripheral clock divided by 6 if Transmit Frame Synchro is input | ||
| 202 | * - Peripheral clock divided by 2 if Transmit Frame Synchro is output | ||
| 203 | * | ||
| 204 | * When the bit clock is output, limit the rate according to the | ||
| 205 | * SSC divider restrictions. | ||
| 206 | */ | ||
| 207 | static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, | ||
| 208 | struct snd_pcm_hw_rule *rule) | ||
| 209 | { | ||
| 210 | struct atmel_ssc_info *ssc_p = rule->private; | ||
| 211 | struct ssc_device *ssc = ssc_p->ssc; | ||
| 212 | struct snd_interval *i = hw_param_interval(params, rule->var); | ||
| 213 | struct snd_interval t; | ||
| 214 | struct snd_ratnum r = { | ||
| 215 | .den_min = 1, | ||
| 216 | .den_max = 4095, | ||
| 217 | .den_step = 1, | ||
| 218 | }; | ||
| 219 | unsigned int num = 0, den = 0; | ||
| 220 | int frame_size; | ||
| 221 | int mck_div = 2; | ||
| 222 | int ret; | ||
| 223 | |||
| 224 | frame_size = snd_soc_params_to_frame_size(params); | ||
| 225 | if (frame_size < 0) | ||
| 226 | return frame_size; | ||
| 227 | |||
| 228 | switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 229 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 230 | if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) | ||
| 231 | && ssc->clk_from_rk_pin) | ||
| 232 | /* Receiver Frame Synchro (i.e. capture) | ||
| 233 | * is output (format is _CFS) and the RK pin | ||
| 234 | * is used for input (format is _CBM_). | ||
| 235 | */ | ||
| 236 | mck_div = 3; | ||
| 237 | break; | ||
| 238 | |||
| 239 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 240 | if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) | ||
| 241 | && !ssc->clk_from_rk_pin) | ||
| 242 | /* Transmit Frame Synchro (i.e. playback) | ||
| 243 | * is input (format is _CFM) and the TK pin | ||
| 244 | * is used for input (format _CBM_ but not | ||
| 245 | * using the RK pin). | ||
| 246 | */ | ||
| 247 | mck_div = 6; | ||
| 248 | break; | ||
| 249 | } | ||
| 250 | |||
| 251 | switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 252 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 253 | r.num = ssc_p->mck_rate / mck_div / frame_size; | ||
| 254 | |||
| 255 | ret = snd_interval_ratnum(i, 1, &r, &num, &den); | ||
| 256 | if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { | ||
| 257 | params->rate_num = num; | ||
| 258 | params->rate_den = den; | ||
| 259 | } | ||
| 260 | break; | ||
| 261 | |||
| 262 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 263 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 264 | t.min = 8000; | ||
| 265 | t.max = ssc_p->mck_rate / mck_div / frame_size; | ||
| 266 | t.openmin = t.openmax = 0; | ||
| 267 | t.integer = 0; | ||
| 268 | ret = snd_interval_refine(i, &t); | ||
| 269 | break; | ||
| 270 | |||
| 271 | default: | ||
| 272 | ret = -EINVAL; | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | |||
| 276 | return ret; | ||
| 277 | } | ||
| 190 | 278 | ||
| 191 | /*-------------------------------------------------------------------------*\ | 279 | /*-------------------------------------------------------------------------*\ |
| 192 | * DAI functions | 280 | * DAI functions |
| @@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, | |||
| 200 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; | 288 | struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; |
| 201 | struct atmel_pcm_dma_params *dma_params; | 289 | struct atmel_pcm_dma_params *dma_params; |
| 202 | int dir, dir_mask; | 290 | int dir, dir_mask; |
| 291 | int ret; | ||
| 203 | 292 | ||
| 204 | pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", | 293 | pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", |
| 205 | ssc_readl(ssc_p->ssc->regs, SR)); | 294 | ssc_readl(ssc_p->ssc->regs, SR)); |
| @@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, | |||
| 207 | /* Enable PMC peripheral clock for this SSC */ | 296 | /* Enable PMC peripheral clock for this SSC */ |
| 208 | pr_debug("atmel_ssc_dai: Starting clock\n"); | 297 | pr_debug("atmel_ssc_dai: Starting clock\n"); |
| 209 | clk_enable(ssc_p->ssc->clk); | 298 | clk_enable(ssc_p->ssc->clk); |
| 299 | ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); | ||
| 210 | 300 | ||
| 211 | /* Reset the SSC to keep it at a clean status */ | 301 | /* Reset the SSC to keep it at a clean status */ |
| 212 | ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); | 302 | ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); |
| @@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, | |||
| 219 | dir_mask = SSC_DIR_MASK_CAPTURE; | 309 | dir_mask = SSC_DIR_MASK_CAPTURE; |
| 220 | } | 310 | } |
| 221 | 311 | ||
| 312 | ret = snd_pcm_hw_rule_add(substream->runtime, 0, | ||
| 313 | SNDRV_PCM_HW_PARAM_RATE, | ||
| 314 | atmel_ssc_hw_rule_rate, | ||
| 315 | ssc_p, | ||
| 316 | SNDRV_PCM_HW_PARAM_FRAME_BITS, | ||
| 317 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); | ||
| 318 | if (ret < 0) { | ||
| 319 | dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); | ||
| 320 | return ret; | ||
| 321 | } | ||
| 322 | |||
| 222 | dma_params = &ssc_dma_params[dai->id][dir]; | 323 | dma_params = &ssc_dma_params[dai->id][dir]; |
| 223 | dma_params->ssc = ssc_p->ssc; | 324 | dma_params->ssc = ssc_p->ssc; |
| 224 | dma_params->substream = substream; | 325 | dma_params->substream = substream; |
| @@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) | |||
| 783 | # define atmel_ssc_resume NULL | 884 | # define atmel_ssc_resume NULL |
| 784 | #endif /* CONFIG_PM */ | 885 | #endif /* CONFIG_PM */ |
| 785 | 886 | ||
| 786 | #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) | ||
| 787 | |||
| 788 | #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ | 887 | #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ |
| 789 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | 888 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
| 790 | 889 | ||
| @@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = { | |||
| 804 | .playback = { | 903 | .playback = { |
| 805 | .channels_min = 1, | 904 | .channels_min = 1, |
| 806 | .channels_max = 2, | 905 | .channels_max = 2, |
| 807 | .rates = ATMEL_SSC_RATES, | 906 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
| 907 | .rate_min = 8000, | ||
| 908 | .rate_max = 384000, | ||
| 808 | .formats = ATMEL_SSC_FORMATS,}, | 909 | .formats = ATMEL_SSC_FORMATS,}, |
| 809 | .capture = { | 910 | .capture = { |
| 810 | .channels_min = 1, | 911 | .channels_min = 1, |
| 811 | .channels_max = 2, | 912 | .channels_max = 2, |
| 812 | .rates = ATMEL_SSC_RATES, | 913 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
| 914 | .rate_min = 8000, | ||
| 915 | .rate_max = 384000, | ||
| 813 | .formats = ATMEL_SSC_FORMATS,}, | 916 | .formats = ATMEL_SSC_FORMATS,}, |
| 814 | .ops = &atmel_ssc_dai_ops, | 917 | .ops = &atmel_ssc_dai_ops, |
| 815 | }; | 918 | }; |
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index b1f08d511495..80b153857a88 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h | |||
| @@ -115,6 +115,7 @@ struct atmel_ssc_info { | |||
| 115 | unsigned short rcmr_period; | 115 | unsigned short rcmr_period; |
| 116 | struct atmel_pcm_dma_params *dma_params[2]; | 116 | struct atmel_pcm_dma_params *dma_params[2]; |
| 117 | struct atmel_ssc_state ssc_state; | 117 | struct atmel_ssc_state ssc_state; |
| 118 | unsigned long mck_rate; | ||
| 118 | }; | 119 | }; |
| 119 | 120 | ||
| 120 | int atmel_ssc_set_audio(int ssc_id); | 121 | int atmel_ssc_set_audio(int ssc_id); |
