diff options
Diffstat (limited to 'sound/soc/sunxi/sun4i-spdif.c')
| -rw-r--r-- | sound/soc/sunxi/sun4i-spdif.c | 70 |
1 files changed, 54 insertions, 16 deletions
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index b849bb7cf58e..2e7ac8ab71bb 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -14,8 +14,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/regmap.h> -#include <linux/of_address.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -177,6 +176,8 @@ struct sun4i_spdif_quirks { unsigned int reg_dac_txdata; bool has_reset; unsigned int val_fctl_ftx; + unsigned int mclk_multiplier; + const char *tx_clk_name; }; struct sun4i_spdif_dev { @@ -202,6 +203,10 @@ static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, quirks->val_fctl_ftx, quirks->val_fctl_ftx); + /* Valid data at the MSB of TXFIFO Register */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, + SUN4I_SPDIF_FCTL_TXIM, 0); + /* clear TX counter */ regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); } @@ -246,8 +251,8 @@ static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream, static int sun4i_spdif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; @@ -283,14 +288,17 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT; + host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case SNDRV_PCM_FORMAT_S20_3LE: fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT; break; case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT; break; default: @@ -314,6 +322,7 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + mclk *= host->quirks->mclk_multiplier; ret = clk_set_rate(host->spdif_clk, mclk); if (ret < 0) { @@ -322,9 +331,6 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, return ret; } - regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, - SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); - switch (rate) { case 22050: case 24000: @@ -348,6 +354,7 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + mclk_div *= host->quirks->mclk_multiplier; reg_val = 0; reg_val |= SUN4I_SPDIF_TXCFG_ASS; @@ -523,9 +530,10 @@ static const struct regmap_config sun4i_spdif_regmap_config = { #define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 -#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_LE) +#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver sun4i_spdif_dai = { .playback = { @@ -541,24 +549,36 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = { static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, + .mclk_multiplier = 1, }; static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, .has_reset = true, + .mclk_multiplier = 1, }; static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, .has_reset = true, + .mclk_multiplier = 4, }; static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, .has_reset = true, + .mclk_multiplier = 1, +}; + +static const struct sun4i_spdif_quirks sun55i_a523_spdif_quirks = { + .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, + .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, + .has_reset = true, + .mclk_multiplier = 1, + .tx_clk_name = "tx", }; static const struct of_device_id sun4i_spdif_of_match[] = { @@ -578,6 +598,20 @@ static const struct of_device_id sun4i_spdif_of_match[] = { .compatible = "allwinner,sun50i-h6-spdif", .data = &sun50i_h6_spdif_quirks, }, + { + .compatible = "allwinner,sun50i-h616-spdif", + /* Essentially the same as the H6, but without RX */ + .data = &sun50i_h6_spdif_quirks, + }, + { + .compatible = "allwinner,sun55i-a523-spdif", + /* + * Almost the same as H6, but has split the TX and RX clocks, + * has a separate reset bit for the RX side, and has some + * expanded features for the RX side. + */ + .data = &sun55i_a523_spdif_quirks, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); @@ -619,6 +653,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev) const struct sun4i_spdif_quirks *quirks; int ret; void __iomem *base; + const char *tx_clk_name = "spdif"; dev_dbg(&pdev->dev, "Entered %s\n", __func__); @@ -655,9 +690,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev) return PTR_ERR(host->apb_clk); } - host->spdif_clk = devm_clk_get(&pdev->dev, "spdif"); + if (quirks->tx_clk_name) + tx_clk_name = quirks->tx_clk_name; + host->spdif_clk = devm_clk_get(&pdev->dev, tx_clk_name); if (IS_ERR(host->spdif_clk)) { - dev_err(&pdev->dev, "failed to get a spdif clock.\n"); + dev_err(&pdev->dev, "failed to get the \"%s\" clock.\n", + tx_clk_name); return PTR_ERR(host->spdif_clk); } @@ -711,18 +749,18 @@ static void sun4i_spdif_remove(struct platform_device *pdev) } static const struct dev_pm_ops sun4i_spdif_pm = { - SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, - sun4i_spdif_runtime_resume, NULL) + RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, + sun4i_spdif_runtime_resume, NULL) }; static struct platform_driver sun4i_spdif_driver = { .driver = { .name = "sun4i-spdif", .of_match_table = sun4i_spdif_of_match, - .pm = &sun4i_spdif_pm, + .pm = pm_ptr(&sun4i_spdif_pm), }, .probe = sun4i_spdif_probe, - .remove_new = sun4i_spdif_remove, + .remove = sun4i_spdif_remove, }; module_platform_driver(sun4i_spdif_driver); |
