[alsa-devel] [PATCH] ASoC: sun4i-i2s: Add quirks for newer SoCs
From: Marcus Cooper codekipper@gmail.com
Newer SoCs have additional functionality so a quirks structure has been added to handle them. So far we've seen the use of a reset controller, a different address for the TXFIFO and varying register changes.
This patch prepares the driver for these changes and adds the reset specifier.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- .../devicetree/bindings/sound/sun4i-i2s.txt | 2 + sound/soc/sunxi/sun4i-i2s.c | 47 ++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt index 7a2c0945fd22..494a881ccd21 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt @@ -18,6 +18,8 @@ Required properties: - "apb" : clock for the I2S bus interface - "mod" : module clock for the I2S controller - #sound-dai-cells : Must be equal to 0 +- resets: reset specifier for the ahb reset (A31 and newer only) +
Example:
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index f24d19526603..80fe4f1d6e3b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -14,9 +14,11 @@ #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/reset.h>
#include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> @@ -92,6 +94,7 @@ struct sun4i_i2s { struct clk *bus_clk; struct clk *mod_clk; struct regmap *regmap; + struct reset_control *rst;
unsigned int mclk_freq;
@@ -104,6 +107,13 @@ struct sun4i_i2s_clk_div { u8 val; };
+struct sun4i_i2s_quirks { + unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */ + bool has_reset; + const struct regmap_config *sun4i_i2s_regmap; + const struct snd_soc_dai_ops *ops; +}; + static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = { { .div = 2, .val = 0 }, { .div = 4, .val = 1 }, @@ -541,7 +551,6 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .ops = &sun4i_i2s_dai_ops, .symmetric_rates = 1, };
@@ -655,6 +664,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev) { struct sun4i_i2s *i2s; struct resource *res; + const struct sun4i_i2s_quirks *quirks; void __iomem *regs; int irq, ret;
@@ -680,8 +690,14 @@ static int sun4i_i2s_probe(struct platform_device *pdev) return PTR_ERR(i2s->bus_clk); }
+ quirks = of_device_get_match_data(&pdev->dev); + if (quirks == NULL) { + dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); + return -ENODEV; + } + i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &sun4i_i2s_regmap_config); + quirks->sun4i_i2s_regmap); if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "Regmap initialisation failed\n"); return PTR_ERR(i2s->regmap); @@ -692,13 +708,25 @@ static int sun4i_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't get our mod clock\n"); return PTR_ERR(i2s->mod_clk); } + - i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; + i2s->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; i2s->playback_dma_data.maxburst = 4;
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; i2s->capture_dma_data.maxburst = 4;
+ if (quirks->has_reset) { + i2s->rst = devm_reset_control_get_optional(&pdev->dev, NULL); + if (IS_ERR(i2s->rst) && PTR_ERR(i2s->rst) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); + goto err_pm_disable; + } + if (!IS_ERR(i2s->rst)) + reset_control_deassert(i2s->rst); + } + pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = sun4i_i2s_runtime_resume(&pdev->dev); @@ -706,6 +734,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev) goto err_pm_disable; }
+ /* Register ops with dai */ + sun4i_i2s_dai.ops = quirks->ops; ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_i2s_component, &sun4i_i2s_dai, 1); @@ -742,8 +772,17 @@ static int sun4i_i2s_remove(struct platform_device *pdev) return 0; }
+static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { + .reg_dac_txdata = SUN4I_I2S_FIFO_TX_REG, + .sun4i_i2s_regmap = &sun4i_i2s_regmap_config, + .ops = &sun4i_i2s_dai_ops, +}; + static const struct of_device_id sun4i_i2s_match[] = { - { .compatible = "allwinner,sun4i-a10-i2s", }, + { + .compatible = "allwinner,sun4i-a10-i2s", + .data = &sun4i_a10_i2s_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
Hi,
On Tue, Dec 20, 2016 at 03:55:24PM +0100, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Newer SoCs have additional functionality so a quirks structure has been added to handle them. So far we've seen the use of a reset controller, a different address for the TXFIFO and varying register changes.
This patch prepares the driver for these changes and adds the reset specifier.
Signed-off-by: Marcus Cooper codekipper@gmail.com
.../devicetree/bindings/sound/sun4i-i2s.txt | 2 + sound/soc/sunxi/sun4i-i2s.c | 47 ++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt index 7a2c0945fd22..494a881ccd21 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt @@ -18,6 +18,8 @@ Required properties: - "apb" : clock for the I2S bus interface - "mod" : module clock for the I2S controller
- #sound-dai-cells : Must be equal to 0
+- resets: reset specifier for the ahb reset (A31 and newer only)
Example:
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index f24d19526603..80fe4f1d6e3b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -14,9 +14,11 @@ #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/reset.h>
#include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> @@ -92,6 +94,7 @@ struct sun4i_i2s { struct clk *bus_clk; struct clk *mod_clk; struct regmap *regmap;
struct reset_control *rst;
unsigned int mclk_freq;
@@ -104,6 +107,13 @@ struct sun4i_i2s_clk_div { u8 val; };
+struct sun4i_i2s_quirks {
- unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
- bool has_reset;
- const struct regmap_config *sun4i_i2s_regmap;
- const struct snd_soc_dai_ops *ops;
+};
This is quite hard to review without actual example of what you'll put in there.
static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = { { .div = 2, .val = 0 }, { .div = 4, .val = 1 }, @@ -541,7 +551,6 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, },
- .ops = &sun4i_i2s_dai_ops, .symmetric_rates = 1,
};
@@ -655,6 +664,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev) { struct sun4i_i2s *i2s; struct resource *res;
- const struct sun4i_i2s_quirks *quirks; void __iomem *regs; int irq, ret;
@@ -680,8 +690,14 @@ static int sun4i_i2s_probe(struct platform_device *pdev) return PTR_ERR(i2s->bus_clk); }
- quirks = of_device_get_match_data(&pdev->dev);
- if (quirks == NULL) {
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
return -ENODEV;
- }
- i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&sun4i_i2s_regmap_config);
if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "Regmap initialisation failed\n"); return PTR_ERR(i2s->regmap);quirks->sun4i_i2s_regmap);
@@ -692,13 +708,25 @@ static int sun4i_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't get our mod clock\n"); return PTR_ERR(i2s->mod_clk); }
Spurious change?
- i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
i2s->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; i2s->playback_dma_data.maxburst = 4;
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; i2s->capture_dma_data.maxburst = 4;
if (quirks->has_reset) {
i2s->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
if (IS_ERR(i2s->rst) && PTR_ERR(i2s->rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
goto err_pm_disable;
}
if (!IS_ERR(i2s->rst))
reset_control_deassert(i2s->rst);
}
That reset line is not optional. The <A31 SoCs don't need it, and you cover that case already, but it is definitely mandatory for the A31.
Thanks, Maxime
On 20 December 2016 at 20:16, Maxime Ripard maxime.ripard@free-electrons.com wrote:
Hi,
On Tue, Dec 20, 2016 at 03:55:24PM +0100, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Newer SoCs have additional functionality so a quirks structure has been added to handle them. So far we've seen the use of a reset controller, a different address for the TXFIFO and varying register changes.
This patch prepares the driver for these changes and adds the reset specifier.
Signed-off-by: Marcus Cooper codekipper@gmail.com
.../devicetree/bindings/sound/sun4i-i2s.txt | 2 + sound/soc/sunxi/sun4i-i2s.c | 47 ++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt index 7a2c0945fd22..494a881ccd21 100644 --- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt +++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt @@ -18,6 +18,8 @@ Required properties: - "apb" : clock for the I2S bus interface - "mod" : module clock for the I2S controller
- #sound-dai-cells : Must be equal to 0
+- resets: reset specifier for the ahb reset (A31 and newer only)
Example:
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index f24d19526603..80fe4f1d6e3b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -14,9 +14,11 @@ #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/reset.h>
#include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> @@ -92,6 +94,7 @@ struct sun4i_i2s { struct clk *bus_clk; struct clk *mod_clk; struct regmap *regmap;
struct reset_control *rst; unsigned int mclk_freq;
@@ -104,6 +107,13 @@ struct sun4i_i2s_clk_div { u8 val; };
+struct sun4i_i2s_quirks {
unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */
bool has_reset;
const struct regmap_config *sun4i_i2s_regmap;
const struct snd_soc_dai_ops *ops;
+};
This is quite hard to review without actual example of what you'll put in there.
Fair enough...I have a patch for the A31 but haven't got any hardware that I can verify it on. I've confirmed on the H3 but I feel like that patch needs some tidying up. That being said...I'll push it as a patch set and we can talk about the setup.
static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = { { .div = 2, .val = 0 }, { .div = 4, .val = 1 }, @@ -541,7 +551,6 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, },
.ops = &sun4i_i2s_dai_ops, .symmetric_rates = 1,
};
@@ -655,6 +664,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev) { struct sun4i_i2s *i2s; struct resource *res;
const struct sun4i_i2s_quirks *quirks; void __iomem *regs; int irq, ret;
@@ -680,8 +690,14 @@ static int sun4i_i2s_probe(struct platform_device *pdev) return PTR_ERR(i2s->bus_clk); }
quirks = of_device_get_match_data(&pdev->dev);
if (quirks == NULL) {
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
return -ENODEV;
}
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&sun4i_i2s_regmap_config);
quirks->sun4i_i2s_regmap); if (IS_ERR(i2s->regmap)) { dev_err(&pdev->dev, "Regmap initialisation failed\n"); return PTR_ERR(i2s->regmap);
@@ -692,13 +708,25 @@ static int sun4i_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't get our mod clock\n"); return PTR_ERR(i2s->mod_clk); }
Spurious change?
ACK
i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
i2s->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; i2s->playback_dma_data.maxburst = 4; i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; i2s->capture_dma_data.maxburst = 4;
if (quirks->has_reset) {
i2s->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
if (IS_ERR(i2s->rst) && PTR_ERR(i2s->rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
goto err_pm_disable;
}
if (!IS_ERR(i2s->rst))
reset_control_deassert(i2s->rst);
}
That reset line is not optional. The <A31 SoCs don't need it, and you cover that case already, but it is definitely mandatory for the A31.
OK..I'll search for a better function call. Thanks for this and the other reviews. CK
Thanks, Maxime
-- Maxime Ripard, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com
participants (3)
-
Code Kipper
-
codekipper@gmail.com
-
Maxime Ripard