| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * ALSA SoC SPDIF Audio Layer |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * Copyright 2015 Marcus Cooper <codekipper@gmail.com> |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Based on the Allwinner SDK driver, released under the GPL. |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 11 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 12 | | - * (at your option) any later version. |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 15 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 | | - * GNU General Public License for more details. |
|---|
| 18 | 9 | */ |
|---|
| 19 | 10 | |
|---|
| 20 | 11 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 74 | 65 | #define SUN4I_SPDIF_FCTL_TXIM BIT(2) |
|---|
| 75 | 66 | #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0) |
|---|
| 76 | 67 | #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) |
|---|
| 68 | + |
|---|
| 69 | +#define SUN50I_H6_SPDIF_FCTL (0x14) |
|---|
| 70 | + #define SUN50I_H6_SPDIF_FCTL_HUB_EN BIT(31) |
|---|
| 71 | + #define SUN50I_H6_SPDIF_FCTL_FTX BIT(30) |
|---|
| 72 | + #define SUN50I_H6_SPDIF_FCTL_FRX BIT(29) |
|---|
| 73 | + #define SUN50I_H6_SPDIF_FCTL_TXTL(v) ((v) << 12) |
|---|
| 74 | + #define SUN50I_H6_SPDIF_FCTL_TXTL_MASK GENMASK(19, 12) |
|---|
| 75 | + #define SUN50I_H6_SPDIF_FCTL_RXTL(v) ((v) << 4) |
|---|
| 76 | + #define SUN50I_H6_SPDIF_FCTL_RXTL_MASK GENMASK(10, 4) |
|---|
| 77 | + #define SUN50I_H6_SPDIF_FCTL_TXIM BIT(2) |
|---|
| 78 | + #define SUN50I_H6_SPDIF_FCTL_RXOM(v) ((v) << 0) |
|---|
| 79 | + #define SUN50I_H6_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) |
|---|
| 77 | 80 | |
|---|
| 78 | 81 | #define SUN4I_SPDIF_FSTA (0x18) |
|---|
| 79 | 82 | #define SUN4I_SPDIF_FSTA_TXE BIT(14) |
|---|
| .. | .. |
|---|
| 161 | 164 | #define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc |
|---|
| 162 | 165 | #define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe |
|---|
| 163 | 166 | |
|---|
| 167 | +/** |
|---|
| 168 | + * struct sun4i_spdif_quirks - Differences between SoC variants. |
|---|
| 169 | + * |
|---|
| 170 | + * @reg_dac_txdata: TX FIFO offset for DMA config. |
|---|
| 171 | + * @has_reset: SoC needs reset deasserted. |
|---|
| 172 | + * @val_fctl_ftx: TX FIFO flush bitmask. |
|---|
| 173 | + */ |
|---|
| 174 | +struct sun4i_spdif_quirks { |
|---|
| 175 | + unsigned int reg_dac_txdata; |
|---|
| 176 | + bool has_reset; |
|---|
| 177 | + unsigned int val_fctl_ftx; |
|---|
| 178 | +}; |
|---|
| 179 | + |
|---|
| 164 | 180 | struct sun4i_spdif_dev { |
|---|
| 165 | 181 | struct platform_device *pdev; |
|---|
| 166 | 182 | struct clk *spdif_clk; |
|---|
| .. | .. |
|---|
| 169 | 185 | struct snd_soc_dai_driver cpu_dai_drv; |
|---|
| 170 | 186 | struct regmap *regmap; |
|---|
| 171 | 187 | struct snd_dmaengine_dai_dma_data dma_params_tx; |
|---|
| 188 | + const struct sun4i_spdif_quirks *quirks; |
|---|
| 172 | 189 | }; |
|---|
| 173 | 190 | |
|---|
| 174 | 191 | static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) |
|---|
| 175 | 192 | { |
|---|
| 193 | + const struct sun4i_spdif_quirks *quirks = host->quirks; |
|---|
| 194 | + |
|---|
| 176 | 195 | /* soft reset SPDIF */ |
|---|
| 177 | 196 | regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); |
|---|
| 178 | 197 | |
|---|
| 179 | 198 | /* flush TX FIFO */ |
|---|
| 180 | 199 | regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, |
|---|
| 181 | | - SUN4I_SPDIF_FCTL_FTX, SUN4I_SPDIF_FCTL_FTX); |
|---|
| 200 | + quirks->val_fctl_ftx, quirks->val_fctl_ftx); |
|---|
| 182 | 201 | |
|---|
| 183 | 202 | /* clear TX counter */ |
|---|
| 184 | 203 | regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); |
|---|
| .. | .. |
|---|
| 224 | 243 | static int sun4i_spdif_startup(struct snd_pcm_substream *substream, |
|---|
| 225 | 244 | struct snd_soc_dai *cpu_dai) |
|---|
| 226 | 245 | { |
|---|
| 227 | | - struct snd_soc_pcm_runtime *rtd = substream->private_data; |
|---|
| 228 | | - struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
|---|
| 246 | + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); |
|---|
| 247 | + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); |
|---|
| 229 | 248 | |
|---|
| 230 | 249 | if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) |
|---|
| 231 | 250 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 405 | 424 | .name = "spdif", |
|---|
| 406 | 425 | }; |
|---|
| 407 | 426 | |
|---|
| 408 | | -struct sun4i_spdif_quirks { |
|---|
| 409 | | - unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ |
|---|
| 410 | | - bool has_reset; |
|---|
| 411 | | -}; |
|---|
| 412 | | - |
|---|
| 413 | 427 | static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { |
|---|
| 414 | 428 | .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, |
|---|
| 429 | + .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, |
|---|
| 415 | 430 | }; |
|---|
| 416 | 431 | |
|---|
| 417 | 432 | static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { |
|---|
| 418 | 433 | .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, |
|---|
| 434 | + .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, |
|---|
| 419 | 435 | .has_reset = true, |
|---|
| 420 | 436 | }; |
|---|
| 421 | 437 | |
|---|
| 422 | 438 | static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { |
|---|
| 423 | 439 | .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, |
|---|
| 440 | + .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, |
|---|
| 424 | 441 | .has_reset = true, |
|---|
| 442 | +}; |
|---|
| 443 | + |
|---|
| 444 | +static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { |
|---|
| 445 | + .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, |
|---|
| 446 | + .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, |
|---|
| 447 | + .has_reset = true, |
|---|
| 425 | 448 | }; |
|---|
| 426 | 449 | |
|---|
| 427 | 450 | static const struct of_device_id sun4i_spdif_of_match[] = { |
|---|
| .. | .. |
|---|
| 436 | 459 | { |
|---|
| 437 | 460 | .compatible = "allwinner,sun8i-h3-spdif", |
|---|
| 438 | 461 | .data = &sun8i_h3_spdif_quirks, |
|---|
| 462 | + }, |
|---|
| 463 | + { |
|---|
| 464 | + .compatible = "allwinner,sun50i-h6-spdif", |
|---|
| 465 | + .data = &sun50i_h6_spdif_quirks, |
|---|
| 439 | 466 | }, |
|---|
| 440 | 467 | { /* sentinel */ } |
|---|
| 441 | 468 | }; |
|---|
| .. | .. |
|---|
| 501 | 528 | dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); |
|---|
| 502 | 529 | return -ENODEV; |
|---|
| 503 | 530 | } |
|---|
| 531 | + host->quirks = quirks; |
|---|
| 504 | 532 | |
|---|
| 505 | 533 | host->regmap = devm_regmap_init_mmio(&pdev->dev, base, |
|---|
| 506 | 534 | &sun4i_spdif_regmap_config); |
|---|
| .. | .. |
|---|
| 527 | 555 | if (quirks->has_reset) { |
|---|
| 528 | 556 | host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, |
|---|
| 529 | 557 | NULL); |
|---|
| 530 | | - if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) { |
|---|
| 558 | + if (PTR_ERR(host->rst) == -EPROBE_DEFER) { |
|---|
| 531 | 559 | ret = -EPROBE_DEFER; |
|---|
| 532 | 560 | dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); |
|---|
| 533 | 561 | return ret; |
|---|