1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Freescale Generic ASoC Sound Card driver with ASRC 4 // 5 // Copyright (C) 2014 Freescale Semiconductor, Inc. 6 // 7 // Author: Nicolin Chen <nicoleotsuka@gmail.com> 8 9 #include <linux/clk.h> 10 #include <linux/i2c.h> 11 #include <linux/module.h> 12 #include <linux/of_platform.h> 13 #if IS_ENABLED(CONFIG_SND_AC97_CODEC) 14 #include <sound/ac97_codec.h> 15 #endif 16 #include <sound/pcm_params.h> 17 #include <sound/soc.h> 18 19 #include "fsl_esai.h" 20 #include "fsl_sai.h" 21 #include "imx-audmux.h" 22 23 #include "../codecs/sgtl5000.h" 24 #include "../codecs/wm8962.h" 25 #include "../codecs/wm8960.h" 26 27 #define CS427x_SYSCLK_MCLK 0 28 29 #define RX 0 30 #define TX 1 31 32 /* Default DAI format without Master and Slave flag */ 33 #define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF) 34 35 /** 36 * CODEC private data 37 * 38 * @mclk_freq: Clock rate of MCLK 39 * @mclk_id: MCLK (or main clock) id for set_sysclk() 40 * @fll_id: FLL (or secordary clock) id for set_sysclk() 41 * @pll_id: PLL id for set_pll() 42 */ 43 struct codec_priv { 44 unsigned long mclk_freq; 45 u32 mclk_id; 46 u32 fll_id; 47 u32 pll_id; 48 }; 49 50 /** 51 * CPU private data 52 * 53 * @sysclk_freq[2]: SYSCLK rates for set_sysclk() 54 * @sysclk_dir[2]: SYSCLK directions for set_sysclk() 55 * @sysclk_id[2]: SYSCLK ids for set_sysclk() 56 * @slot_width: Slot width of each frame 57 * 58 * Note: [1] for tx and [0] for rx 59 */ 60 struct cpu_priv { 61 unsigned long sysclk_freq[2]; 62 u32 sysclk_dir[2]; 63 u32 sysclk_id[2]; 64 u32 slot_width; 65 }; 66 67 /** 68 * Freescale Generic ASOC card private data 69 * 70 * @dai_link[3]: DAI link structure including normal one and DPCM link 71 * @pdev: platform device pointer 72 * @codec_priv: CODEC private data 73 * @cpu_priv: CPU private data 74 * @card: ASoC card structure 75 * @sample_rate: Current sample rate 76 * @sample_format: Current sample format 77 * @asrc_rate: ASRC sample rate used by Back-Ends 78 * @asrc_format: ASRC sample format used by Back-Ends 79 * @dai_fmt: DAI format between CPU and CODEC 80 * @name: Card name 81 */ 82 83 struct fsl_asoc_card_priv { 84 struct snd_soc_dai_link dai_link[3]; 85 struct platform_device *pdev; 86 struct codec_priv codec_priv; 87 struct cpu_priv cpu_priv; 88 struct snd_soc_card card; 89 u32 sample_rate; 90 snd_pcm_format_t sample_format; 91 u32 asrc_rate; 92 snd_pcm_format_t asrc_format; 93 u32 dai_fmt; 94 char name[32]; 95 }; 96 97 /** 98 * This dapm route map exsits for DPCM link only. 99 * The other routes shall go through Device Tree. 100 * 101 * Note: keep all ASRC routes in the second half 102 * to drop them easily for non-ASRC cases. 103 */ 104 static const struct snd_soc_dapm_route audio_map[] = { 105 /* 1st half -- Normal DAPM routes */ 106 {"Playback", NULL, "CPU-Playback"}, 107 {"CPU-Capture", NULL, "Capture"}, 108 /* 2nd half -- ASRC DAPM routes */ 109 {"CPU-Playback", NULL, "ASRC-Playback"}, 110 {"ASRC-Capture", NULL, "CPU-Capture"}, 111 }; 112 113 static const struct snd_soc_dapm_route audio_map_ac97[] = { 114 /* 1st half -- Normal DAPM routes */ 115 {"Playback", NULL, "AC97 Playback"}, 116 {"AC97 Capture", NULL, "Capture"}, 117 /* 2nd half -- ASRC DAPM routes */ 118 {"AC97 Playback", NULL, "ASRC-Playback"}, 119 {"ASRC-Capture", NULL, "AC97 Capture"}, 120 }; 121 122 /* Add all possible widgets into here without being redundant */ 123 static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { 124 SND_SOC_DAPM_LINE("Line Out Jack", NULL), 125 SND_SOC_DAPM_LINE("Line In Jack", NULL), 126 SND_SOC_DAPM_HP("Headphone Jack", NULL), 127 SND_SOC_DAPM_SPK("Ext Spk", NULL), 128 SND_SOC_DAPM_MIC("Mic Jack", NULL), 129 SND_SOC_DAPM_MIC("AMIC", NULL), 130 SND_SOC_DAPM_MIC("DMIC", NULL), 131 }; 132 133 static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv) 134 { 135 return priv->dai_fmt == SND_SOC_DAIFMT_AC97; 136 } 137 138 static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, 139 struct snd_pcm_hw_params *params) 140 { 141 struct snd_soc_pcm_runtime *rtd = substream->private_data; 142 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); 143 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 144 struct cpu_priv *cpu_priv = &priv->cpu_priv; 145 struct device *dev = rtd->card->dev; 146 int ret; 147 148 priv->sample_rate = params_rate(params); 149 priv->sample_format = params_format(params); 150 151 /* 152 * If codec-dai is DAI Master and all configurations are already in the 153 * set_bias_level(), bypass the remaining settings in hw_params(). 154 * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS. 155 */ 156 if ((priv->card.set_bias_level && 157 priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) || 158 fsl_asoc_card_is_ac97(priv)) 159 return 0; 160 161 /* Specific configurations of DAIs starts from here */ 162 ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx], 163 cpu_priv->sysclk_freq[tx], 164 cpu_priv->sysclk_dir[tx]); 165 if (ret && ret != -ENOTSUPP) { 166 dev_err(dev, "failed to set sysclk for cpu dai\n"); 167 return ret; 168 } 169 170 if (cpu_priv->slot_width) { 171 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 172 cpu_priv->slot_width); 173 if (ret && ret != -ENOTSUPP) { 174 dev_err(dev, "failed to set TDM slot for cpu dai\n"); 175 return ret; 176 } 177 } 178 179 return 0; 180 } 181 182 static const struct snd_soc_ops fsl_asoc_card_ops = { 183 .hw_params = fsl_asoc_card_hw_params, 184 }; 185 186 static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, 187 struct snd_pcm_hw_params *params) 188 { 189 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); 190 struct snd_interval *rate; 191 struct snd_mask *mask; 192 193 rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 194 rate->max = rate->min = priv->asrc_rate; 195 196 mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 197 snd_mask_none(mask); 198 snd_mask_set_format(mask, priv->asrc_format); 199 200 return 0; 201 } 202 203 SND_SOC_DAILINK_DEFS(hifi, 204 DAILINK_COMP_ARRAY(COMP_EMPTY()), 205 DAILINK_COMP_ARRAY(COMP_EMPTY()), 206 DAILINK_COMP_ARRAY(COMP_EMPTY())); 207 208 SND_SOC_DAILINK_DEFS(hifi_fe, 209 DAILINK_COMP_ARRAY(COMP_EMPTY()), 210 DAILINK_COMP_ARRAY(COMP_DUMMY()), 211 DAILINK_COMP_ARRAY(COMP_EMPTY())); 212 213 SND_SOC_DAILINK_DEFS(hifi_be, 214 DAILINK_COMP_ARRAY(COMP_EMPTY()), 215 DAILINK_COMP_ARRAY(COMP_EMPTY()), 216 DAILINK_COMP_ARRAY(COMP_DUMMY())); 217 218 static struct snd_soc_dai_link fsl_asoc_card_dai[] = { 219 /* Default ASoC DAI Link*/ 220 { 221 .name = "HiFi", 222 .stream_name = "HiFi", 223 .ops = &fsl_asoc_card_ops, 224 SND_SOC_DAILINK_REG(hifi), 225 }, 226 /* DPCM Link between Front-End and Back-End (Optional) */ 227 { 228 .name = "HiFi-ASRC-FE", 229 .stream_name = "HiFi-ASRC-FE", 230 .dpcm_playback = 1, 231 .dpcm_capture = 1, 232 .dynamic = 1, 233 SND_SOC_DAILINK_REG(hifi_fe), 234 }, 235 { 236 .name = "HiFi-ASRC-BE", 237 .stream_name = "HiFi-ASRC-BE", 238 .be_hw_params_fixup = be_hw_params_fixup, 239 .ops = &fsl_asoc_card_ops, 240 .dpcm_playback = 1, 241 .dpcm_capture = 1, 242 .no_pcm = 1, 243 SND_SOC_DAILINK_REG(hifi_be), 244 }, 245 }; 246 247 static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, 248 struct snd_soc_dapm_context *dapm, 249 enum snd_soc_bias_level level) 250 { 251 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); 252 struct snd_soc_pcm_runtime *rtd; 253 struct snd_soc_dai *codec_dai; 254 struct codec_priv *codec_priv = &priv->codec_priv; 255 struct device *dev = card->dev; 256 unsigned int pll_out; 257 int ret; 258 259 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); 260 codec_dai = rtd->codec_dai; 261 if (dapm->dev != codec_dai->dev) 262 return 0; 263 264 switch (level) { 265 case SND_SOC_BIAS_PREPARE: 266 if (dapm->bias_level != SND_SOC_BIAS_STANDBY) 267 break; 268 269 if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) 270 pll_out = priv->sample_rate * 384; 271 else 272 pll_out = priv->sample_rate * 256; 273 274 ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 275 codec_priv->mclk_id, 276 codec_priv->mclk_freq, pll_out); 277 if (ret) { 278 dev_err(dev, "failed to start FLL: %d\n", ret); 279 return ret; 280 } 281 282 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id, 283 pll_out, SND_SOC_CLOCK_IN); 284 if (ret && ret != -ENOTSUPP) { 285 dev_err(dev, "failed to set SYSCLK: %d\n", ret); 286 return ret; 287 } 288 break; 289 290 case SND_SOC_BIAS_STANDBY: 291 if (dapm->bias_level != SND_SOC_BIAS_PREPARE) 292 break; 293 294 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, 295 codec_priv->mclk_freq, 296 SND_SOC_CLOCK_IN); 297 if (ret && ret != -ENOTSUPP) { 298 dev_err(dev, "failed to switch away from FLL: %d\n", ret); 299 return ret; 300 } 301 302 ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0); 303 if (ret) { 304 dev_err(dev, "failed to stop FLL: %d\n", ret); 305 return ret; 306 } 307 break; 308 309 default: 310 break; 311 } 312 313 return 0; 314 } 315 316 static int fsl_asoc_card_audmux_init(struct device_node *np, 317 struct fsl_asoc_card_priv *priv) 318 { 319 struct device *dev = &priv->pdev->dev; 320 u32 int_ptcr = 0, ext_ptcr = 0; 321 int int_port, ext_port; 322 int ret; 323 324 ret = of_property_read_u32(np, "mux-int-port", &int_port); 325 if (ret) { 326 dev_err(dev, "mux-int-port missing or invalid\n"); 327 return ret; 328 } 329 ret = of_property_read_u32(np, "mux-ext-port", &ext_port); 330 if (ret) { 331 dev_err(dev, "mux-ext-port missing or invalid\n"); 332 return ret; 333 } 334 335 /* 336 * The port numbering in the hardware manual starts at 1, while 337 * the AUDMUX API expects it starts at 0. 338 */ 339 int_port--; 340 ext_port--; 341 342 /* 343 * Use asynchronous mode (6 wires) for all cases except AC97. 344 * If only 4 wires are needed, just set SSI into 345 * synchronous mode and enable 4 PADs in IOMUX. 346 */ 347 switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 348 case SND_SOC_DAIFMT_CBM_CFM: 349 int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | 350 IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | 351 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 352 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 353 IMX_AUDMUX_V2_PTCR_RFSDIR | 354 IMX_AUDMUX_V2_PTCR_RCLKDIR | 355 IMX_AUDMUX_V2_PTCR_TFSDIR | 356 IMX_AUDMUX_V2_PTCR_TCLKDIR; 357 break; 358 case SND_SOC_DAIFMT_CBM_CFS: 359 int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | 360 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 361 IMX_AUDMUX_V2_PTCR_RCLKDIR | 362 IMX_AUDMUX_V2_PTCR_TCLKDIR; 363 ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | 364 IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | 365 IMX_AUDMUX_V2_PTCR_RFSDIR | 366 IMX_AUDMUX_V2_PTCR_TFSDIR; 367 break; 368 case SND_SOC_DAIFMT_CBS_CFM: 369 int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | 370 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 371 IMX_AUDMUX_V2_PTCR_RFSDIR | 372 IMX_AUDMUX_V2_PTCR_TFSDIR; 373 ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | 374 IMX_AUDMUX_V2_PTCR_TCSEL(int_port) | 375 IMX_AUDMUX_V2_PTCR_RCLKDIR | 376 IMX_AUDMUX_V2_PTCR_TCLKDIR; 377 break; 378 case SND_SOC_DAIFMT_CBS_CFS: 379 ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | 380 IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | 381 IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | 382 IMX_AUDMUX_V2_PTCR_TCSEL(int_port) | 383 IMX_AUDMUX_V2_PTCR_RFSDIR | 384 IMX_AUDMUX_V2_PTCR_RCLKDIR | 385 IMX_AUDMUX_V2_PTCR_TFSDIR | 386 IMX_AUDMUX_V2_PTCR_TCLKDIR; 387 break; 388 default: 389 if (!fsl_asoc_card_is_ac97(priv)) 390 return -EINVAL; 391 } 392 393 if (fsl_asoc_card_is_ac97(priv)) { 394 int_ptcr = IMX_AUDMUX_V2_PTCR_SYN | 395 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 396 IMX_AUDMUX_V2_PTCR_TCLKDIR; 397 ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN | 398 IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | 399 IMX_AUDMUX_V2_PTCR_TFSDIR; 400 } 401 402 /* Asynchronous mode can not be set along with RCLKDIR */ 403 if (!fsl_asoc_card_is_ac97(priv)) { 404 unsigned int pdcr = 405 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port); 406 407 ret = imx_audmux_v2_configure_port(int_port, 0, 408 pdcr); 409 if (ret) { 410 dev_err(dev, "audmux internal port setup failed\n"); 411 return ret; 412 } 413 } 414 415 ret = imx_audmux_v2_configure_port(int_port, int_ptcr, 416 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); 417 if (ret) { 418 dev_err(dev, "audmux internal port setup failed\n"); 419 return ret; 420 } 421 422 if (!fsl_asoc_card_is_ac97(priv)) { 423 unsigned int pdcr = 424 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port); 425 426 ret = imx_audmux_v2_configure_port(ext_port, 0, 427 pdcr); 428 if (ret) { 429 dev_err(dev, "audmux external port setup failed\n"); 430 return ret; 431 } 432 } 433 434 ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr, 435 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); 436 if (ret) { 437 dev_err(dev, "audmux external port setup failed\n"); 438 return ret; 439 } 440 441 return 0; 442 } 443 444 static int fsl_asoc_card_late_probe(struct snd_soc_card *card) 445 { 446 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); 447 struct snd_soc_pcm_runtime *rtd = list_first_entry( 448 &card->rtd_list, struct snd_soc_pcm_runtime, list); 449 struct snd_soc_dai *codec_dai = rtd->codec_dai; 450 struct codec_priv *codec_priv = &priv->codec_priv; 451 struct device *dev = card->dev; 452 int ret; 453 454 if (fsl_asoc_card_is_ac97(priv)) { 455 #if IS_ENABLED(CONFIG_SND_AC97_CODEC) 456 struct snd_soc_component *component = rtd->codec_dai->component; 457 struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); 458 459 /* 460 * Use slots 3/4 for S/PDIF so SSI won't try to enable 461 * other slots and send some samples there 462 * due to SLOTREQ bits for S/PDIF received from codec 463 */ 464 snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 465 AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4); 466 #endif 467 468 return 0; 469 } 470 471 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, 472 codec_priv->mclk_freq, SND_SOC_CLOCK_IN); 473 if (ret && ret != -ENOTSUPP) { 474 dev_err(dev, "failed to set sysclk in %s\n", __func__); 475 return ret; 476 } 477 478 return 0; 479 } 480 481 static int fsl_asoc_card_probe(struct platform_device *pdev) 482 { 483 struct device_node *cpu_np, *codec_np, *asrc_np; 484 struct device_node *np = pdev->dev.of_node; 485 struct platform_device *asrc_pdev = NULL; 486 struct platform_device *cpu_pdev; 487 struct fsl_asoc_card_priv *priv; 488 struct i2c_client *codec_dev; 489 const char *codec_dai_name; 490 u32 width; 491 int ret; 492 493 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 494 if (!priv) 495 return -ENOMEM; 496 497 cpu_np = of_parse_phandle(np, "audio-cpu", 0); 498 /* Give a chance to old DT binding */ 499 if (!cpu_np) 500 cpu_np = of_parse_phandle(np, "ssi-controller", 0); 501 if (!cpu_np) { 502 dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); 503 ret = -EINVAL; 504 goto fail; 505 } 506 507 cpu_pdev = of_find_device_by_node(cpu_np); 508 if (!cpu_pdev) { 509 dev_err(&pdev->dev, "failed to find CPU DAI device\n"); 510 ret = -EINVAL; 511 goto fail; 512 } 513 514 codec_np = of_parse_phandle(np, "audio-codec", 0); 515 if (codec_np) 516 codec_dev = of_find_i2c_device_by_node(codec_np); 517 else 518 codec_dev = NULL; 519 520 asrc_np = of_parse_phandle(np, "audio-asrc", 0); 521 if (asrc_np) 522 asrc_pdev = of_find_device_by_node(asrc_np); 523 524 /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ 525 if (codec_dev) { 526 struct clk *codec_clk = clk_get(&codec_dev->dev, NULL); 527 528 if (!IS_ERR(codec_clk)) { 529 priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); 530 clk_put(codec_clk); 531 } 532 } 533 534 /* Default sample rate and format, will be updated in hw_params() */ 535 priv->sample_rate = 44100; 536 priv->sample_format = SNDRV_PCM_FORMAT_S16_LE; 537 538 /* Assign a default DAI format, and allow each card to overwrite it */ 539 priv->dai_fmt = DAI_FMT_BASE; 540 541 /* Diversify the card configurations */ 542 if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { 543 codec_dai_name = "cs42888"; 544 priv->card.set_bias_level = NULL; 545 priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; 546 priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; 547 priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; 548 priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; 549 priv->cpu_priv.slot_width = 32; 550 priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 551 } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { 552 codec_dai_name = "cs4271-hifi"; 553 priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; 554 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 555 } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { 556 codec_dai_name = "sgtl5000"; 557 priv->codec_priv.mclk_id = SGTL5000_SYSCLK; 558 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 559 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { 560 codec_dai_name = "wm8962"; 561 priv->card.set_bias_level = fsl_asoc_card_set_bias_level; 562 priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; 563 priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; 564 priv->codec_priv.pll_id = WM8962_FLL; 565 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 566 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { 567 codec_dai_name = "wm8960-hifi"; 568 priv->card.set_bias_level = fsl_asoc_card_set_bias_level; 569 priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; 570 priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; 571 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 572 } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { 573 codec_dai_name = "ac97-hifi"; 574 priv->card.set_bias_level = NULL; 575 priv->dai_fmt = SND_SOC_DAIFMT_AC97; 576 } else { 577 dev_err(&pdev->dev, "unknown Device Tree compatible\n"); 578 ret = -EINVAL; 579 goto asrc_fail; 580 } 581 582 if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { 583 dev_err(&pdev->dev, "failed to find codec device\n"); 584 ret = -EINVAL; 585 goto asrc_fail; 586 } 587 588 /* Common settings for corresponding Freescale CPU DAI driver */ 589 if (of_node_name_eq(cpu_np, "ssi")) { 590 /* Only SSI needs to configure AUDMUX */ 591 ret = fsl_asoc_card_audmux_init(np, priv); 592 if (ret) { 593 dev_err(&pdev->dev, "failed to init audmux\n"); 594 goto asrc_fail; 595 } 596 } else if (of_node_name_eq(cpu_np, "esai")) { 597 priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; 598 priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; 599 } else if (of_node_name_eq(cpu_np, "sai")) { 600 priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; 601 priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; 602 } 603 604 snprintf(priv->name, sizeof(priv->name), "%s-audio", 605 fsl_asoc_card_is_ac97(priv) ? "ac97" : 606 codec_dev->name); 607 608 /* Initialize sound card */ 609 priv->pdev = pdev; 610 priv->card.dev = &pdev->dev; 611 priv->card.name = priv->name; 612 priv->card.dai_link = priv->dai_link; 613 priv->card.dapm_routes = fsl_asoc_card_is_ac97(priv) ? 614 audio_map_ac97 : audio_map; 615 priv->card.late_probe = fsl_asoc_card_late_probe; 616 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); 617 priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; 618 priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); 619 620 /* Drop the second half of DAPM routes -- ASRC */ 621 if (!asrc_pdev) 622 priv->card.num_dapm_routes /= 2; 623 624 memcpy(priv->dai_link, fsl_asoc_card_dai, 625 sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); 626 627 ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing"); 628 if (ret) { 629 dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); 630 goto asrc_fail; 631 } 632 633 /* Normal DAI Link */ 634 priv->dai_link[0].cpus->of_node = cpu_np; 635 priv->dai_link[0].codecs->dai_name = codec_dai_name; 636 637 if (!fsl_asoc_card_is_ac97(priv)) 638 priv->dai_link[0].codecs->of_node = codec_np; 639 else { 640 u32 idx; 641 642 ret = of_property_read_u32(cpu_np, "cell-index", &idx); 643 if (ret) { 644 dev_err(&pdev->dev, 645 "cannot get CPU index property\n"); 646 goto asrc_fail; 647 } 648 649 priv->dai_link[0].codecs->name = 650 devm_kasprintf(&pdev->dev, GFP_KERNEL, 651 "ac97-codec.%u", 652 (unsigned int)idx); 653 if (!priv->dai_link[0].codecs->name) { 654 ret = -ENOMEM; 655 goto asrc_fail; 656 } 657 } 658 659 priv->dai_link[0].platforms->of_node = cpu_np; 660 priv->dai_link[0].dai_fmt = priv->dai_fmt; 661 priv->card.num_links = 1; 662 663 if (asrc_pdev) { 664 /* DPCM DAI Links only if ASRC exsits */ 665 priv->dai_link[1].cpus->of_node = asrc_np; 666 priv->dai_link[1].platforms->of_node = asrc_np; 667 priv->dai_link[2].codecs->dai_name = codec_dai_name; 668 priv->dai_link[2].codecs->of_node = codec_np; 669 priv->dai_link[2].codecs->name = 670 priv->dai_link[0].codecs->name; 671 priv->dai_link[2].cpus->of_node = cpu_np; 672 priv->dai_link[2].dai_fmt = priv->dai_fmt; 673 priv->card.num_links = 3; 674 675 ret = of_property_read_u32(asrc_np, "fsl,asrc-rate", 676 &priv->asrc_rate); 677 if (ret) { 678 dev_err(&pdev->dev, "failed to get output rate\n"); 679 ret = -EINVAL; 680 goto asrc_fail; 681 } 682 683 ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width); 684 if (ret) { 685 dev_err(&pdev->dev, "failed to get output rate\n"); 686 ret = -EINVAL; 687 goto asrc_fail; 688 } 689 690 if (width == 24) 691 priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE; 692 else 693 priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE; 694 } 695 696 /* Finish card registering */ 697 platform_set_drvdata(pdev, priv); 698 snd_soc_card_set_drvdata(&priv->card, priv); 699 700 ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); 701 if (ret && ret != -EPROBE_DEFER) 702 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); 703 704 asrc_fail: 705 of_node_put(asrc_np); 706 of_node_put(codec_np); 707 put_device(&cpu_pdev->dev); 708 fail: 709 of_node_put(cpu_np); 710 711 return ret; 712 } 713 714 static const struct of_device_id fsl_asoc_card_dt_ids[] = { 715 { .compatible = "fsl,imx-audio-ac97", }, 716 { .compatible = "fsl,imx-audio-cs42888", }, 717 { .compatible = "fsl,imx-audio-cs427x", }, 718 { .compatible = "fsl,imx-audio-sgtl5000", }, 719 { .compatible = "fsl,imx-audio-wm8962", }, 720 { .compatible = "fsl,imx-audio-wm8960", }, 721 {} 722 }; 723 MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); 724 725 static struct platform_driver fsl_asoc_card_driver = { 726 .probe = fsl_asoc_card_probe, 727 .driver = { 728 .name = "fsl-asoc-card", 729 .pm = &snd_soc_pm_ops, 730 .of_match_table = fsl_asoc_card_dt_ids, 731 }, 732 }; 733 module_platform_driver(fsl_asoc_card_driver); 734 735 MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC"); 736 MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>"); 737 MODULE_ALIAS("platform:fsl-asoc-card"); 738 MODULE_LICENSE("GPL"); 739