Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: fsl-asoc-card: add S/PDIF controller support

Merge series from Elinor Montmasson <elinor.montmasson@savoirfairelinux.com>:

This is a series of patches aiming to make the machine driver
`fsl-asoc-card` compatible with S/PDIF controllers on imx boards. The
main goal is to allow the use of S/PDIF controllers with ASRC modules.

The `imx-spdif` machine driver already has specific support for S/PDIF
controllers but doesn't support using an ASRC with it. However, the
`fsl-asoc-card` machine driver has the necessary code to create a sound
card which can use an ASRC module.
It is then possible to extend the support for S/PDIF audio cards by
merging the `imx-spdif` driver into `fsl-asoc-card`.

The first three patches adapt the `fsl-asoc-card` driver to support
multiple codec use cases.
The driver can get 2 codec phandles from the device tree, and
codec-related variables are doubled.
`for_each_codecs` macros are also used when possible to ease adding
other multi-codec use cases in the future.
It makes possible to use the two S/PDIF dummy codec drivers
`spdif_receiver` and `spdif_transmitter` instead of `snd-soc-dummy`,
which was used in `imx-spdif`.

The fourth patch merges the S/PDIF support from `imx-spdif` to
`fsl-asoc-card`.
`fsl-asoc-card` offers the same functionalities as `imx-spdif` did, but
this merge also extends the S/PDIF support with the possibility of using
an ASRC.
Compatible "fsl,imx-audio-spdif" is kept, but `fsl-asoc-card` uses
different DT properties compared to `imx-spdif`:
* The "spdif-controller" property from `imx-spdif` is named "audio-cpu"
in `fsl-asoc-card`.
* `fsl-asoc-card` uses codecs explicitly declared in DT with
"audio-codec". With an S/PDIF, codec drivers `spdif_transmitter` and
`spdif_receiver` should be used. Driver `imx-spdif` used instead the
dummy codec and a pair of boolean properties, "spdif-in" and
"spdif-out".
Backward compatibility is therefore implemented in `fsl-asoc-card`.
However, it is recommended to use the new properties when needed.
Especially, declaring and using S/PDIF transmitter and/or receiver nodes
is better than using the dummy codec.

The last three patches update the device tree bindings of
`fsl-asoc-card` and update all in-tree device trees to use the
`fsl-asoc-card` properties.
Note that as the old properties are still supported:
* previous versions of in-tree device trees are still supported.
* out-of-tree device trees are still supported.

This series of patches was successfully built for arm64 and x86 on top
of the latest "for-next" branch of the ASoC git tree on the 26th of June
2024.
These modifications have also been tested on an i.MX8MN evaluation board
with a linux kernel RT v6.1.26-rt8.

+306 -313
-66
Documentation/devicetree/bindings/sound/fsl,imx-audio-spdif.yaml
··· 1 - # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 - %YAML 1.2 3 - --- 4 - $id: http://devicetree.org/schemas/sound/fsl,imx-audio-spdif.yaml# 5 - $schema: http://devicetree.org/meta-schemas/core.yaml# 6 - 7 - title: Freescale i.MX audio complex with S/PDIF transceiver 8 - 9 - maintainers: 10 - - Shengjiu Wang <shengjiu.wang@nxp.com> 11 - 12 - properties: 13 - compatible: 14 - oneOf: 15 - - items: 16 - - enum: 17 - - fsl,imx-sabreauto-spdif 18 - - fsl,imx6sx-sdb-spdif 19 - - const: fsl,imx-audio-spdif 20 - - enum: 21 - - fsl,imx-audio-spdif 22 - 23 - model: 24 - $ref: /schemas/types.yaml#/definitions/string 25 - description: User specified audio sound card name 26 - 27 - spdif-controller: 28 - $ref: /schemas/types.yaml#/definitions/phandle 29 - description: The phandle of the i.MX S/PDIF controller 30 - 31 - spdif-out: 32 - type: boolean 33 - description: 34 - If present, the transmitting function of S/PDIF will be enabled, 35 - indicating there's a physical S/PDIF out connector or jack on the 36 - board or it's connecting to some other IP block, such as an HDMI 37 - encoder or display-controller. 38 - 39 - spdif-in: 40 - type: boolean 41 - description: 42 - If present, the receiving function of S/PDIF will be enabled, 43 - indicating there is a physical S/PDIF in connector/jack on the board. 44 - 45 - required: 46 - - compatible 47 - - model 48 - - spdif-controller 49 - 50 - anyOf: 51 - - required: 52 - - spdif-in 53 - - required: 54 - - spdif-out 55 - 56 - additionalProperties: false 57 - 58 - examples: 59 - - | 60 - sound-spdif { 61 - compatible = "fsl,imx-audio-spdif"; 62 - model = "imx-spdif"; 63 - spdif-controller = <&spdif>; 64 - spdif-out; 65 - spdif-in; 66 - };
+49 -4
Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml
··· 67 67 - fsl,imx-audio-wm8962 68 68 - items: 69 69 - enum: 70 + - fsl,imx-sabreauto-spdif 71 + - fsl,imx6sx-sdb-spdif 72 + - const: fsl,imx-audio-spdif 73 + - items: 74 + - enum: 70 75 - fsl,imx-audio-ac97 71 76 - fsl,imx-audio-cs42888 72 77 - fsl,imx-audio-cs427x ··· 86 81 - fsl,imx-audio-wm8960 87 82 - fsl,imx-audio-wm8962 88 83 - fsl,imx-audio-wm8958 84 + - fsl,imx-audio-spdif 89 85 90 86 model: 91 87 $ref: /schemas/types.yaml#/definitions/string ··· 99 93 need to add ASRC support via DPCM. 100 94 101 95 audio-codec: 102 - $ref: /schemas/types.yaml#/definitions/phandle 103 - description: The phandle of an audio codec 96 + $ref: /schemas/types.yaml#/definitions/phandle-array 97 + description: | 98 + The phandle of an audio codec. 99 + With "fsl,imx-audio-spdif", either SPDIF audio codec spdif_transmitter, 100 + spdif_receiver or both. 101 + minItems: 1 102 + maxItems: 2 103 + items: 104 + maxItems: 1 104 105 105 106 audio-cpu: 106 107 $ref: /schemas/types.yaml#/definitions/phandle ··· 163 150 description: dai-link uses bit clock inversion. 164 151 165 152 mclk-id: 166 - $ref: /schemas/types.yaml#/definitions/uint32 167 - description: main clock id, specific for each card configuration. 153 + $ref: /schemas/types.yaml#/definitions/uint32-array 154 + description: Main clock id for each codec, specific for each card configuration. 155 + minItems: 1 156 + maxItems: 2 168 157 169 158 mux-int-port: 170 159 $ref: /schemas/types.yaml#/definitions/uint32 ··· 181 166 ssi-controller: 182 167 $ref: /schemas/types.yaml#/definitions/phandle 183 168 description: The phandle of an CPU DAI controller 169 + 170 + spdif-controller: 171 + $ref: /schemas/types.yaml#/definitions/phandle 172 + deprecated: true 173 + description: The phandle of an S/PDIF CPU DAI controller. 174 + 175 + spdif-out: 176 + type: boolean 177 + deprecated: true 178 + description: | 179 + If present, the transmitting function of S/PDIF will be enabled, 180 + indicating there's a physical S/PDIF out connector or jack on the 181 + board or it's connecting to some other IP block, such as an HDMI 182 + encoder or display-controller. 183 + 184 + spdif-in: 185 + type: boolean 186 + deprecated: true 187 + description: | 188 + If present, the receiving function of S/PDIF will be enabled, 189 + indicating there is a physical S/PDIF in connector/jack on the board. 184 190 185 191 required: 186 192 - compatible ··· 230 194 "AIN1R", "Line In Jack", 231 195 "AIN2L", "Line In Jack", 232 196 "AIN2R", "Line In Jack"; 197 + }; 198 + 199 + - | 200 + sound-spdif-asrc { 201 + compatible = "fsl,imx-audio-spdif"; 202 + model = "spdif-asrc-audio"; 203 + audio-cpu = <&spdif>; 204 + audio-asrc = <&easrc>; 205 + audio-codec = <&spdifdit>, <&spdifdir>; 233 206 };
-1
arch/arm/configs/imx_v6_v7_defconfig
··· 311 311 CONFIG_SND_SOC_EUKREA_TLV320=y 312 312 CONFIG_SND_SOC_IMX_ES8328=y 313 313 CONFIG_SND_SOC_IMX_SGTL5000=y 314 - CONFIG_SND_SOC_IMX_SPDIF=y 315 314 CONFIG_SND_SOC_FSL_ASOC_CARD=y 316 315 CONFIG_SND_SOC_AC97_CODEC=y 317 316 CONFIG_SND_SOC_CS42XX8_I2C=y
-1
arch/arm64/configs/defconfig
··· 940 940 CONFIG_SND_SOC_FSL_EASRC=m 941 941 CONFIG_SND_IMX_SOC=m 942 942 CONFIG_SND_SOC_IMX_SGTL5000=m 943 - CONFIG_SND_SOC_IMX_SPDIF=m 944 943 CONFIG_SND_SOC_FSL_ASOC_CARD=m 945 944 CONFIG_SND_SOC_IMX_AUDMIX=m 946 945 CONFIG_SND_SOC_MT8183=m
+1 -9
sound/soc/fsl/Kconfig
··· 303 303 SND_SOC_FSL_ASOC_CARD and SND_SOC_SGTL5000 to use the newer 304 304 driver. 305 305 306 - config SND_SOC_IMX_SPDIF 307 - tristate "SoC Audio support for i.MX boards with S/PDIF" 308 - select SND_SOC_IMX_PCM_DMA 309 - select SND_SOC_FSL_SPDIF 310 - help 311 - SoC Audio support for i.MX boards with S/PDIF 312 - Say Y if you want to add support for SoC audio on an i.MX board with 313 - a S/DPDIF. 314 - 315 306 config SND_SOC_FSL_ASOC_CARD 316 307 tristate "Generic ASoC Sound Card with ASRC support" 317 308 depends on OF && I2C ··· 314 323 select SND_SOC_FSL_ESAI 315 324 select SND_SOC_FSL_SAI 316 325 select SND_SOC_FSL_SSI 326 + select SND_SOC_FSL_SPDIF 317 327 select SND_SOC_TLV320AIC31XX 318 328 select SND_SOC_WM8994 319 329 select MFD_WM8994
-2
sound/soc/fsl/Makefile
··· 67 67 snd-soc-eukrea-tlv320-y := eukrea-tlv320.o 68 68 snd-soc-imx-es8328-y := imx-es8328.o 69 69 snd-soc-imx-sgtl5000-y := imx-sgtl5000.o 70 - snd-soc-imx-spdif-y := imx-spdif.o 71 70 snd-soc-imx-audmix-y := imx-audmix.o 72 71 snd-soc-imx-hdmi-y := imx-hdmi.o 73 72 snd-soc-imx-rpmsg-y := imx-rpmsg.o ··· 75 76 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o 76 77 obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o 77 78 obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o 78 - obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o 79 79 obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o 80 80 obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o 81 81 obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
+256 -127
sound/soc/fsl/fsl-asoc-card.c
··· 99 99 struct simple_util_jack hp_jack; 100 100 struct simple_util_jack mic_jack; 101 101 struct platform_device *pdev; 102 - struct codec_priv codec_priv; 102 + struct codec_priv codec_priv[2]; 103 103 struct cpu_priv cpu_priv; 104 104 struct snd_soc_card card; 105 105 u8 streams; ··· 172 172 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 173 173 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); 174 174 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 175 - struct codec_priv *codec_priv = &priv->codec_priv; 175 + struct codec_priv *codec_priv; 176 + struct snd_soc_dai *codec_dai; 176 177 struct cpu_priv *cpu_priv = &priv->cpu_priv; 177 178 struct device *dev = rtd->card->dev; 178 179 unsigned int pll_out; 180 + int codec_idx; 179 181 int ret; 180 182 181 183 priv->sample_rate = params_rate(params); ··· 210 208 } 211 209 212 210 /* Specific configuration for PLL */ 213 - if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { 214 - if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) 215 - pll_out = priv->sample_rate * 384; 216 - else 217 - pll_out = priv->sample_rate * 256; 211 + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { 212 + codec_priv = &priv->codec_priv[codec_idx]; 218 213 219 - ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), 220 - codec_priv->pll_id, 221 - codec_priv->mclk_id, 222 - codec_priv->mclk_freq, pll_out); 223 - if (ret) { 224 - dev_err(dev, "failed to start FLL: %d\n", ret); 225 - goto fail; 226 - } 214 + if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { 215 + if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) 216 + pll_out = priv->sample_rate * 384; 217 + else 218 + pll_out = priv->sample_rate * 256; 227 219 228 - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), 229 - codec_priv->fll_id, 230 - pll_out, SND_SOC_CLOCK_IN); 220 + ret = snd_soc_dai_set_pll(codec_dai, 221 + codec_priv->pll_id, 222 + codec_priv->mclk_id, 223 + codec_priv->mclk_freq, pll_out); 224 + if (ret) { 225 + dev_err(dev, "failed to start FLL: %d\n", ret); 226 + goto fail; 227 + } 231 228 232 - if (ret && ret != -ENOTSUPP) { 233 - dev_err(dev, "failed to set SYSCLK: %d\n", ret); 234 - goto fail; 229 + ret = snd_soc_dai_set_sysclk(codec_dai, 230 + codec_priv->fll_id, 231 + pll_out, SND_SOC_CLOCK_IN); 232 + 233 + if (ret && ret != -ENOTSUPP) { 234 + dev_err(dev, "failed to set SYSCLK: %d\n", ret); 235 + goto fail; 236 + } 235 237 } 236 238 } 237 239 ··· 250 244 { 251 245 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 252 246 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); 253 - struct codec_priv *codec_priv = &priv->codec_priv; 247 + struct codec_priv *codec_priv; 248 + struct snd_soc_dai *codec_dai; 254 249 struct device *dev = rtd->card->dev; 250 + int codec_idx; 255 251 int ret; 256 252 257 253 priv->streams &= ~BIT(substream->stream); 258 254 259 - if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { 260 - /* Force freq to be free_freq to avoid error message in codec */ 261 - ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0), 262 - codec_priv->mclk_id, 263 - codec_priv->free_freq, 264 - SND_SOC_CLOCK_IN); 265 - if (ret) { 266 - dev_err(dev, "failed to switch away from FLL: %d\n", ret); 267 - return ret; 268 - } 255 + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { 256 + codec_priv = &priv->codec_priv[codec_idx]; 269 257 270 - ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0), 271 - codec_priv->pll_id, 0, 0, 0); 272 - if (ret && ret != -ENOTSUPP) { 273 - dev_err(dev, "failed to stop FLL: %d\n", ret); 274 - return ret; 258 + if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { 259 + /* Force freq to be free_freq to avoid error message in codec */ 260 + ret = snd_soc_dai_set_sysclk(codec_dai, 261 + codec_priv->mclk_id, 262 + codec_priv->free_freq, 263 + SND_SOC_CLOCK_IN); 264 + if (ret) { 265 + dev_err(dev, "failed to switch away from FLL: %d\n", ret); 266 + return ret; 267 + } 268 + 269 + ret = snd_soc_dai_set_pll(codec_dai, 270 + codec_priv->pll_id, 0, 0, 0); 271 + if (ret && ret != -ENOTSUPP) { 272 + dev_err(dev, "failed to stop FLL: %d\n", ret); 273 + return ret; 274 + } 275 275 } 276 276 } 277 277 ··· 308 296 309 297 SND_SOC_DAILINK_DEFS(hifi, 310 298 DAILINK_COMP_ARRAY(COMP_EMPTY()), 311 - DAILINK_COMP_ARRAY(COMP_EMPTY()), 299 + DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()), 312 300 DAILINK_COMP_ARRAY(COMP_EMPTY())); 313 301 314 302 SND_SOC_DAILINK_DEFS(hifi_fe, ··· 318 306 319 307 SND_SOC_DAILINK_DEFS(hifi_be, 320 308 DAILINK_COMP_ARRAY(COMP_EMPTY()), 321 - DAILINK_COMP_ARRAY(COMP_EMPTY())); 309 + DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY())); 322 310 323 311 static const struct snd_soc_dai_link fsl_asoc_card_dai[] = { 324 312 /* Default ASoC DAI Link*/ ··· 477 465 return 0; 478 466 } 479 467 468 + static int fsl_asoc_card_spdif_init(struct device_node *codec_np[], 469 + struct device_node *cpu_np, 470 + const char *codec_dai_name[], 471 + struct fsl_asoc_card_priv *priv) 472 + { 473 + struct device *dev = &priv->pdev->dev; 474 + struct device_node *np = dev->of_node; 475 + 476 + if (!of_node_name_eq(cpu_np, "spdif")) { 477 + dev_err(dev, "CPU phandle invalid, should be an SPDIF device\n"); 478 + return -EINVAL; 479 + } 480 + 481 + priv->dai_link[0].playback_only = true; 482 + priv->dai_link[0].capture_only = true; 483 + 484 + for (int i = 0; i < 2; i++) { 485 + if (!codec_np[i]) 486 + break; 487 + 488 + if (of_device_is_compatible(codec_np[i], "linux,spdif-dit")) { 489 + priv->dai_link[0].capture_only = false; 490 + codec_dai_name[i] = "dit-hifi"; 491 + } else if (of_device_is_compatible(codec_np[i], "linux,spdif-dir")) { 492 + priv->dai_link[0].playback_only = false; 493 + codec_dai_name[i] = "dir-hifi"; 494 + } 495 + } 496 + 497 + // Old SPDIF DT binding 498 + if (!codec_np[0]) { 499 + codec_dai_name[0] = snd_soc_dummy_dlc.dai_name; 500 + if (of_property_read_bool(np, "spdif-out")) 501 + priv->dai_link[0].capture_only = false; 502 + if (of_property_read_bool(np, "spdif-in")) 503 + priv->dai_link[0].playback_only = false; 504 + } 505 + 506 + if (priv->dai_link[0].playback_only && priv->dai_link[0].capture_only) { 507 + dev_err(dev, "no enabled S/PDIF DAI link\n"); 508 + return -EINVAL; 509 + } 510 + 511 + if (priv->dai_link[0].playback_only) { 512 + priv->dai_link[1].dpcm_capture = false; 513 + priv->dai_link[2].dpcm_capture = false; 514 + priv->card.dapm_routes = audio_map_tx; 515 + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); 516 + } else if (priv->dai_link[0].capture_only) { 517 + priv->dai_link[1].dpcm_playback = false; 518 + priv->dai_link[2].dpcm_playback = false; 519 + priv->card.dapm_routes = audio_map_rx; 520 + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); 521 + } 522 + 523 + // No DAPM routes with old bindings and dummy codec 524 + if (!codec_np[0]) { 525 + priv->card.dapm_routes = NULL; 526 + priv->card.num_dapm_routes = 0; 527 + } 528 + 529 + if (codec_np[0] && codec_np[1]) { 530 + priv->dai_link[0].num_codecs = 2; 531 + priv->dai_link[2].num_codecs = 2; 532 + } 533 + 534 + return 0; 535 + } 536 + 480 537 static int hp_jack_event(struct notifier_block *nb, unsigned long event, 481 538 void *data) 482 539 { ··· 585 504 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card); 586 505 struct snd_soc_pcm_runtime *rtd = list_first_entry( 587 506 &card->rtd_list, struct snd_soc_pcm_runtime, list); 588 - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 589 - struct codec_priv *codec_priv = &priv->codec_priv; 507 + struct snd_soc_dai *codec_dai; 508 + struct codec_priv *codec_priv; 590 509 struct device *dev = card->dev; 510 + int codec_idx; 591 511 int ret; 592 512 593 513 if (fsl_asoc_card_is_ac97(priv)) { ··· 608 526 return 0; 609 527 } 610 528 611 - ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, 612 - codec_priv->mclk_freq, SND_SOC_CLOCK_IN); 613 - if (ret && ret != -ENOTSUPP) { 614 - dev_err(dev, "failed to set sysclk in %s\n", __func__); 615 - return ret; 616 - } 529 + for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) { 530 + codec_priv = &priv->codec_priv[codec_idx]; 617 531 618 - if (!IS_ERR_OR_NULL(codec_priv->mclk)) 619 - clk_prepare_enable(codec_priv->mclk); 532 + ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, 533 + codec_priv->mclk_freq, SND_SOC_CLOCK_IN); 534 + if (ret && ret != -ENOTSUPP) { 535 + dev_err(dev, "failed to set sysclk in %s\n", __func__); 536 + return ret; 537 + } 538 + 539 + if (!IS_ERR_OR_NULL(codec_priv->mclk)) 540 + clk_prepare_enable(codec_priv->mclk); 541 + } 620 542 621 543 return 0; 622 544 } 623 545 624 546 static int fsl_asoc_card_probe(struct platform_device *pdev) 625 547 { 626 - struct device_node *cpu_np, *codec_np, *asrc_np; 548 + struct device_node *cpu_np, *asrc_np; 549 + struct snd_soc_dai_link_component *codec_comp; 550 + struct device_node *codec_np[2]; 627 551 struct device_node *np = pdev->dev.of_node; 628 552 struct platform_device *asrc_pdev = NULL; 629 553 struct device_node *bitclkprovider = NULL; 630 554 struct device_node *frameprovider = NULL; 631 555 struct platform_device *cpu_pdev; 632 556 struct fsl_asoc_card_priv *priv; 633 - struct device *codec_dev = NULL; 634 - const char *codec_dai_name; 635 - const char *codec_dev_name; 557 + struct device *codec_dev[2] = { NULL, NULL }; 558 + const char *codec_dai_name[2]; 559 + const char *codec_dev_name[2]; 636 560 u32 asrc_fmt = 0; 561 + int codec_idx; 637 562 u32 width; 638 563 int ret; 639 564 ··· 651 562 priv->pdev = pdev; 652 563 653 564 cpu_np = of_parse_phandle(np, "audio-cpu", 0); 654 - /* Give a chance to old DT binding */ 565 + /* Give a chance to old DT bindings */ 655 566 if (!cpu_np) 656 567 cpu_np = of_parse_phandle(np, "ssi-controller", 0); 568 + if (!cpu_np) 569 + cpu_np = of_parse_phandle(np, "spdif-controller", 0); 657 570 if (!cpu_np) { 658 571 dev_err(&pdev->dev, "CPU phandle missing or invalid\n"); 659 572 ret = -EINVAL; ··· 669 578 goto fail; 670 579 } 671 580 672 - codec_np = of_parse_phandle(np, "audio-codec", 0); 673 - if (codec_np) { 674 - struct platform_device *codec_pdev; 675 - struct i2c_client *codec_i2c; 581 + codec_np[0] = of_parse_phandle(np, "audio-codec", 0); 582 + codec_np[1] = of_parse_phandle(np, "audio-codec", 1); 676 583 677 - codec_i2c = of_find_i2c_device_by_node(codec_np); 678 - if (codec_i2c) { 679 - codec_dev = &codec_i2c->dev; 680 - codec_dev_name = codec_i2c->name; 681 - } 682 - if (!codec_dev) { 683 - codec_pdev = of_find_device_by_node(codec_np); 684 - if (codec_pdev) { 685 - codec_dev = &codec_pdev->dev; 686 - codec_dev_name = codec_pdev->name; 584 + for (codec_idx = 0; codec_idx < 2; codec_idx++) { 585 + if (codec_np[codec_idx]) { 586 + struct platform_device *codec_pdev; 587 + struct i2c_client *codec_i2c; 588 + 589 + codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]); 590 + if (codec_i2c) { 591 + codec_dev[codec_idx] = &codec_i2c->dev; 592 + codec_dev_name[codec_idx] = codec_i2c->name; 593 + } 594 + if (!codec_dev[codec_idx]) { 595 + codec_pdev = of_find_device_by_node(codec_np[codec_idx]); 596 + if (codec_pdev) { 597 + codec_dev[codec_idx] = &codec_pdev->dev; 598 + codec_dev_name[codec_idx] = codec_pdev->name; 599 + } 687 600 } 688 601 } 689 602 } ··· 697 602 asrc_pdev = of_find_device_by_node(asrc_np); 698 603 699 604 /* Get the MCLK rate only, and leave it controlled by CODEC drivers */ 700 - if (codec_dev) { 701 - struct clk *codec_clk = clk_get(codec_dev, NULL); 605 + for (codec_idx = 0; codec_idx < 2; codec_idx++) { 606 + if (codec_dev[codec_idx]) { 607 + struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL); 702 608 703 - if (!IS_ERR(codec_clk)) { 704 - priv->codec_priv.mclk_freq = clk_get_rate(codec_clk); 705 - clk_put(codec_clk); 609 + if (!IS_ERR(codec_clk)) { 610 + priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk); 611 + clk_put(codec_clk); 612 + } 706 613 } 707 614 } 708 615 ··· 717 620 718 621 memcpy(priv->dai_link, fsl_asoc_card_dai, 719 622 sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); 623 + priv->dai_link[0].num_codecs = 1; 624 + priv->dai_link[2].num_codecs = 1; 720 625 721 626 priv->card.dapm_routes = audio_map; 722 627 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); 723 628 priv->card.driver_name = DRIVER_NAME; 724 629 725 - priv->codec_priv.fll_id = -1; 726 - priv->codec_priv.pll_id = -1; 630 + for (codec_idx = 0; codec_idx < 2; codec_idx++) { 631 + priv->codec_priv[codec_idx].fll_id = -1; 632 + priv->codec_priv[codec_idx].pll_id = -1; 633 + } 727 634 728 635 /* Diversify the card configurations */ 729 636 if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { 730 - codec_dai_name = "cs42888"; 731 - priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq; 732 - priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq; 637 + codec_dai_name[0] = "cs42888"; 638 + priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq; 639 + priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq; 733 640 priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; 734 641 priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; 735 642 priv->cpu_priv.slot_width = 32; 736 643 priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; 737 644 } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { 738 - codec_dai_name = "cs4271-hifi"; 739 - priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; 645 + codec_dai_name[0] = "cs4271-hifi"; 646 + priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK; 740 647 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 741 648 } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { 742 - codec_dai_name = "sgtl5000"; 743 - priv->codec_priv.mclk_id = SGTL5000_SYSCLK; 649 + codec_dai_name[0] = "sgtl5000"; 650 + priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK; 744 651 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 745 652 } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { 746 - codec_dai_name = "tlv320aic32x4-hifi"; 653 + codec_dai_name[0] = "tlv320aic32x4-hifi"; 747 654 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 748 655 } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { 749 - codec_dai_name = "tlv320dac31xx-hifi"; 656 + codec_dai_name[0] = "tlv320dac31xx-hifi"; 750 657 priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 751 658 priv->dai_link[1].dpcm_capture = 0; 752 659 priv->dai_link[2].dpcm_capture = 0; ··· 759 658 priv->card.dapm_routes = audio_map_tx; 760 659 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); 761 660 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { 762 - codec_dai_name = "wm8962"; 763 - priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; 764 - priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; 765 - priv->codec_priv.pll_id = WM8962_FLL; 661 + codec_dai_name[0] = "wm8962"; 662 + priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK; 663 + priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL; 664 + priv->codec_priv[0].pll_id = WM8962_FLL; 766 665 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 767 666 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { 768 - codec_dai_name = "wm8960-hifi"; 769 - priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; 770 - priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; 667 + codec_dai_name[0] = "wm8960-hifi"; 668 + priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO; 669 + priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO; 771 670 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 772 671 } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { 773 - codec_dai_name = "ac97-hifi"; 672 + codec_dai_name[0] = "ac97-hifi"; 774 673 priv->dai_fmt = SND_SOC_DAIFMT_AC97; 775 674 priv->card.dapm_routes = audio_map_ac97; 776 675 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97); 777 676 } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { 778 - codec_dai_name = "fsl-mqs-dai"; 677 + codec_dai_name[0] = "fsl-mqs-dai"; 779 678 priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | 780 679 SND_SOC_DAIFMT_CBC_CFC | 781 680 SND_SOC_DAIFMT_NB_NF; ··· 784 683 priv->card.dapm_routes = audio_map_tx; 785 684 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); 786 685 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { 787 - codec_dai_name = "wm8524-hifi"; 686 + codec_dai_name[0] = "wm8524-hifi"; 788 687 priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; 789 688 priv->dai_link[1].dpcm_capture = 0; 790 689 priv->dai_link[2].dpcm_capture = 0; ··· 792 691 priv->card.dapm_routes = audio_map_tx; 793 692 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); 794 693 } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { 795 - codec_dai_name = "si476x-codec"; 694 + codec_dai_name[0] = "si476x-codec"; 796 695 priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; 797 696 priv->card.dapm_routes = audio_map_rx; 798 697 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); 799 698 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) { 800 - codec_dai_name = "wm8994-aif1"; 699 + codec_dai_name[0] = "wm8994-aif1"; 801 700 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 802 - priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1; 803 - priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1; 804 - priv->codec_priv.pll_id = WM8994_FLL1; 805 - priv->codec_priv.free_freq = priv->codec_priv.mclk_freq; 701 + priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1; 702 + priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1; 703 + priv->codec_priv[0].pll_id = WM8994_FLL1; 704 + priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq; 806 705 priv->card.dapm_routes = NULL; 807 706 priv->card.num_dapm_routes = 0; 808 707 } else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) { 809 - codec_dai_name = "nau8822-hifi"; 810 - priv->codec_priv.mclk_id = NAU8822_CLK_MCLK; 811 - priv->codec_priv.fll_id = NAU8822_CLK_PLL; 812 - priv->codec_priv.pll_id = NAU8822_CLK_PLL; 708 + codec_dai_name[0] = "nau8822-hifi"; 709 + priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; 710 + priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; 711 + priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; 813 712 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 814 - if (codec_dev) 815 - priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL); 713 + if (codec_dev[0]) 714 + priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); 816 715 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { 817 - codec_dai_name = "wm8904-hifi"; 818 - priv->codec_priv.mclk_id = WM8904_FLL_MCLK; 819 - priv->codec_priv.fll_id = WM8904_CLK_FLL; 820 - priv->codec_priv.pll_id = WM8904_FLL_MCLK; 716 + codec_dai_name[0] = "wm8904-hifi"; 717 + priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK; 718 + priv->codec_priv[0].fll_id = WM8904_CLK_FLL; 719 + priv->codec_priv[0].pll_id = WM8904_FLL_MCLK; 821 720 priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 721 + } else if (of_device_is_compatible(np, "fsl,imx-audio-spdif")) { 722 + ret = fsl_asoc_card_spdif_init(codec_np, cpu_np, codec_dai_name, priv); 723 + if (ret) 724 + goto asrc_fail; 822 725 } else { 823 726 dev_err(&pdev->dev, "unknown Device Tree compatible\n"); 824 727 ret = -EINVAL; ··· 833 728 * Allow setting mclk-id from the device-tree node. Otherwise, the 834 729 * default value for each card configuration is used. 835 730 */ 836 - of_property_read_u32(np, "mclk-id", &priv->codec_priv.mclk_id); 731 + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { 732 + of_property_read_u32_index(np, "mclk-id", codec_idx, 733 + &priv->codec_priv[codec_idx].mclk_id); 734 + } 837 735 838 736 /* Format info from DT is optional. */ 839 737 snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider); 840 738 if (bitclkprovider || frameprovider) { 841 739 unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL); 740 + bool codec_bitclkprovider = false; 741 + bool codec_frameprovider = false; 842 742 843 - if (codec_np == bitclkprovider) 844 - daifmt |= (codec_np == frameprovider) ? 743 + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { 744 + if (bitclkprovider && codec_np[codec_idx] == bitclkprovider) 745 + codec_bitclkprovider = true; 746 + if (frameprovider && codec_np[codec_idx] == frameprovider) 747 + codec_frameprovider = true; 748 + } 749 + 750 + if (codec_bitclkprovider) 751 + daifmt |= (codec_frameprovider) ? 845 752 SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC; 846 753 else 847 - daifmt |= (codec_np == frameprovider) ? 754 + daifmt |= (codec_frameprovider) ? 848 755 SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC; 849 756 850 757 /* Override dai_fmt with value from DT */ ··· 872 755 of_node_put(bitclkprovider); 873 756 of_node_put(frameprovider); 874 757 875 - if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { 758 + if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0] 759 + && codec_dai_name[0] != snd_soc_dummy_dlc.dai_name) { 876 760 dev_dbg(&pdev->dev, "failed to find codec device\n"); 877 761 ret = -EPROBE_DEFER; 878 762 goto asrc_fail; ··· 912 794 ret = snd_soc_of_parse_card_name(&priv->card, "model"); 913 795 if (ret) { 914 796 snprintf(priv->name, sizeof(priv->name), "%s-audio", 915 - fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name); 797 + fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]); 916 798 priv->card.name = priv->name; 917 799 } 918 800 priv->card.dai_link = priv->dai_link; ··· 934 816 935 817 /* Normal DAI Link */ 936 818 priv->dai_link[0].cpus->of_node = cpu_np; 937 - priv->dai_link[0].codecs->dai_name = codec_dai_name; 819 + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { 820 + codec_comp->dai_name = codec_dai_name[codec_idx]; 821 + } 938 822 939 - if (!fsl_asoc_card_is_ac97(priv)) 940 - priv->dai_link[0].codecs->of_node = codec_np; 941 - else { 823 + // Old SPDIF DT binding support 824 + if (codec_dai_name[0] == snd_soc_dummy_dlc.dai_name) 825 + priv->dai_link[0].codecs[0].name = snd_soc_dummy_dlc.name; 826 + 827 + if (!fsl_asoc_card_is_ac97(priv)) { 828 + for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) { 829 + codec_comp->of_node = codec_np[codec_idx]; 830 + } 831 + } else { 942 832 u32 idx; 943 833 944 834 ret = of_property_read_u32(cpu_np, "cell-index", &idx); ··· 956 830 goto asrc_fail; 957 831 } 958 832 959 - priv->dai_link[0].codecs->name = 833 + priv->dai_link[0].codecs[0].name = 960 834 devm_kasprintf(&pdev->dev, GFP_KERNEL, 961 835 "ac97-codec.%u", 962 836 (unsigned int)idx); 963 - if (!priv->dai_link[0].codecs->name) { 837 + if (!priv->dai_link[0].codecs[0].name) { 964 838 ret = -ENOMEM; 965 839 goto asrc_fail; 966 840 } ··· 974 848 /* DPCM DAI Links only if ASRC exists */ 975 849 priv->dai_link[1].cpus->of_node = asrc_np; 976 850 priv->dai_link[1].platforms->of_node = asrc_np; 977 - priv->dai_link[2].codecs->dai_name = codec_dai_name; 978 - priv->dai_link[2].codecs->of_node = codec_np; 979 - priv->dai_link[2].codecs->name = 980 - priv->dai_link[0].codecs->name; 851 + for_each_link_codecs((&(priv->dai_link[2])), codec_idx, codec_comp) { 852 + codec_comp->dai_name = priv->dai_link[0].codecs[codec_idx].dai_name; 853 + codec_comp->of_node = priv->dai_link[0].codecs[codec_idx].of_node; 854 + codec_comp->name = priv->dai_link[0].codecs[codec_idx].name; 855 + } 981 856 priv->dai_link[2].cpus->of_node = cpu_np; 982 857 priv->dai_link[2].dai_fmt = priv->dai_fmt; 983 858 priv->card.num_links = 3; ··· 1048 921 1049 922 asrc_fail: 1050 923 of_node_put(asrc_np); 1051 - of_node_put(codec_np); 924 + of_node_put(codec_np[0]); 925 + of_node_put(codec_np[1]); 1052 926 put_device(&cpu_pdev->dev); 1053 927 fail: 1054 928 of_node_put(cpu_np); ··· 1072 944 { .compatible = "fsl,imx-audio-wm8958", }, 1073 945 { .compatible = "fsl,imx-audio-nau8822", }, 1074 946 { .compatible = "fsl,imx-audio-wm8904", }, 947 + { .compatible = "fsl,imx-audio-spdif", }, 1075 948 {} 1076 949 }; 1077 950 MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
-103
sound/soc/fsl/imx-spdif.c
··· 1 - // SPDX-License-Identifier: GPL-2.0+ 2 - // 3 - // Copyright (C) 2013 Freescale Semiconductor, Inc. 4 - 5 - #include <linux/module.h> 6 - #include <linux/of_platform.h> 7 - #include <sound/soc.h> 8 - 9 - struct imx_spdif_data { 10 - struct snd_soc_dai_link dai; 11 - struct snd_soc_card card; 12 - }; 13 - 14 - static int imx_spdif_audio_probe(struct platform_device *pdev) 15 - { 16 - struct device_node *spdif_np, *np = pdev->dev.of_node; 17 - struct imx_spdif_data *data; 18 - struct snd_soc_dai_link_component *comp; 19 - int ret = 0; 20 - 21 - spdif_np = of_parse_phandle(np, "spdif-controller", 0); 22 - if (!spdif_np) { 23 - dev_err(&pdev->dev, "failed to find spdif-controller\n"); 24 - ret = -EINVAL; 25 - goto end; 26 - } 27 - 28 - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 29 - comp = devm_kzalloc(&pdev->dev, sizeof(*comp), GFP_KERNEL); 30 - if (!data || !comp) { 31 - ret = -ENOMEM; 32 - goto end; 33 - } 34 - 35 - /* 36 - * CPU == Platform 37 - * platform is using soc-generic-dmaengine-pcm 38 - */ 39 - data->dai.cpus = 40 - data->dai.platforms = comp; 41 - data->dai.codecs = &snd_soc_dummy_dlc; 42 - 43 - data->dai.num_cpus = 1; 44 - data->dai.num_codecs = 1; 45 - data->dai.num_platforms = 1; 46 - 47 - data->dai.name = "S/PDIF PCM"; 48 - data->dai.stream_name = "S/PDIF PCM"; 49 - data->dai.cpus->of_node = spdif_np; 50 - data->dai.playback_only = true; 51 - data->dai.capture_only = true; 52 - 53 - if (of_property_read_bool(np, "spdif-out")) 54 - data->dai.capture_only = false; 55 - 56 - if (of_property_read_bool(np, "spdif-in")) 57 - data->dai.playback_only = false; 58 - 59 - if (data->dai.playback_only && data->dai.capture_only) { 60 - dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n"); 61 - goto end; 62 - } 63 - 64 - data->card.dev = &pdev->dev; 65 - data->card.dai_link = &data->dai; 66 - data->card.num_links = 1; 67 - data->card.owner = THIS_MODULE; 68 - 69 - ret = snd_soc_of_parse_card_name(&data->card, "model"); 70 - if (ret) 71 - goto end; 72 - 73 - ret = devm_snd_soc_register_card(&pdev->dev, &data->card); 74 - if (ret) 75 - dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); 76 - 77 - end: 78 - of_node_put(spdif_np); 79 - 80 - return ret; 81 - } 82 - 83 - static const struct of_device_id imx_spdif_dt_ids[] = { 84 - { .compatible = "fsl,imx-audio-spdif", }, 85 - { /* sentinel */ } 86 - }; 87 - MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); 88 - 89 - static struct platform_driver imx_spdif_driver = { 90 - .driver = { 91 - .name = "imx-spdif", 92 - .pm = &snd_soc_pm_ops, 93 - .of_match_table = imx_spdif_dt_ids, 94 - }, 95 - .probe = imx_spdif_audio_probe, 96 - }; 97 - 98 - module_platform_driver(imx_spdif_driver); 99 - 100 - MODULE_AUTHOR("Freescale Semiconductor, Inc."); 101 - MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver"); 102 - MODULE_LICENSE("GPL v2"); 103 - MODULE_ALIAS("platform:imx-spdif");