summaryrefslogtreecommitdiff
path: root/sound/soc/sunxi/sun4i-spdif.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sunxi/sun4i-spdif.c')
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c70
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);