Merge remote-tracking branches 'asoc/topic/simple', 'asoc/topic/spear', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Fri, 1 Sep 2017 11:13:06 +0000 (12:13 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 1 Sep 2017 11:13:06 +0000 (12:13 +0100)
18 files changed:
Documentation/devicetree/bindings/sound/simple-card.txt
Documentation/devicetree/bindings/sound/simple-scu-card.txt
Documentation/devicetree/bindings/sound/sun4i-i2s.txt
include/sound/simple_card_utils.h
sound/soc/codecs/sta32x.c
sound/soc/generic/audio-graph-card.c
sound/soc/generic/audio-graph-scu-card.c
sound/soc/generic/simple-card-utils.c
sound/soc/generic/simple-scu-card.c
sound/soc/spear/spdif_in.c
sound/soc/spear/spdif_out.c
sound/soc/stm/stm32_i2s.c
sound/soc/stm/stm32_sai.c
sound/soc/stm/stm32_spdifrx.c
sound/soc/sunxi/sun4i-codec.c
sound/soc/sunxi/sun4i-i2s.c
sound/soc/sunxi/sun4i-spdif.c
sound/soc/sunxi/sun8i-codec.c

index c7a93931fad27c7d88069925ae5125434f3e8f74..166f2290233b2f55f11dabe42ea9362e0697e30d 100644 (file)
@@ -86,6 +86,9 @@ Optional CPU/CODEC subnodes properties:
                                          in dai startup() and disabled with
                                          clk_disable_unprepare() in dai
                                          shutdown().
+- system-clock-direction-out           : specifies clock direction as 'out' on
+                                         initialization. It is useful for some aCPUs with
+                                         fixed clocks.
 
 Example 1 - single DAI link:
 
index 327d229a51b27e126fad58678a75dd2c803fed54..32f8dbce5241d4a9ad612c7c47e0890fc3438625 100644 (file)
@@ -24,6 +24,7 @@ Optional subnode properties:
 - simple-audio-card,convert-rate       : platform specified sampling rate convert
 - simple-audio-card,convert-channels   : platform specified converted channel size (2 - 8 ch)
 - simple-audio-card,prefix             : see routing
+- simple-audio-card,widgets            : Please refer to widgets.txt.
 - simple-audio-card,routing            : A list of the connections between audio components.
                                          Each entry is a pair of strings, the first being the connection's sink,
                                          the second being the connection's source. Valid names for sources.
index ee21da865771f303c9b21a288b5b1a293662aac7..fc5da6080759d26e0d65fc0c576f128c0bc70dfc 100644 (file)
@@ -8,6 +8,7 @@ Required properties:
 - compatible: should be one of the following:
    - "allwinner,sun4i-a10-i2s"
    - "allwinner,sun6i-a31-i2s"
+   - "allwinner,sun8i-h3-i2s"
 - reg: physical base address of the controller and length of memory mapped
   region.
 - interrupts: should contain the I2S interrupt.
@@ -22,6 +23,7 @@ Required properties:
 
 Required properties for the following compatibles:
        - "allwinner,sun6i-a31-i2s"
+       - "allwinner,sun8i-h3-i2s"
 - resets: phandle to the reset line for this codec
 
 Example:
index 42c6a6ac3ce60d6ab81fc4e726e6dfe0129804ff..7e25afce6566fe315496979c9da9f6176d3fae20 100644 (file)
@@ -15,6 +15,7 @@
 struct asoc_simple_dai {
        const char *name;
        unsigned int sysclk;
+       int clk_direction;
        int slots;
        int slot_width;
        unsigned int tx_slot_mask;
index 0790ae8530d96dba77d25165aefb8f5f2ad88779..5b888476d9ff110b26cc6d42f1a7c808f05027ea 100644 (file)
@@ -847,8 +847,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
                msleep(300);
                sta32x_watchdog_stop(sta32x);
 
-               if (sta32x->gpiod_nreset)
-                       gpiod_set_value(sta32x->gpiod_nreset, 0);
+               gpiod_set_value(sta32x->gpiod_nreset, 0);
 
                regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
                                       sta32x->supplies);
index 2f7c8cf6f123f5c2a8c4fcee7f76e21fd0b473e0..488c52f9405fcd2fb7ec909d7fdda4bbf8b36e81 100644 (file)
@@ -325,6 +325,7 @@ MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
 static struct platform_driver asoc_graph_card = {
        .driver = {
                .name = "asoc-audio-graph-card",
+               .pm = &snd_soc_pm_ops,
                .of_match_table = asoc_graph_of_match,
        },
        .probe = asoc_graph_card_probe,
index 06b6fcf0513ed31a401dc1967ca9de711392e73c..a967aa143d51864cb03e3c9f8fcb9c7ea72c8a51 100644 (file)
@@ -401,6 +401,7 @@ MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
 static struct platform_driver asoc_graph_card = {
        .driver = {
                .name = "asoc-audio-graph-scu-card",
+               .pm = &snd_soc_pm_ops,
                .of_match_table = asoc_graph_of_match,
        },
        .probe = asoc_graph_card_probe,
index d72f7d58102f7666740866eb3b8b213604df4e54..3751a07de6aac1c6bab6e670abd9c91832fd7f51 100644 (file)
@@ -196,7 +196,11 @@ int asoc_simple_card_parse_clk(struct device *dev,
                        simple_dai->sysclk = clk_get_rate(clk);
        }
 
-       dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk);
+       if (of_property_read_bool(node, "system-clock-direction-out"))
+               simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
+
+       dev_dbg(dev, "%s : sysclk = %d, direction %d\n", name,
+               simple_dai->sysclk, simple_dai->clk_direction);
 
        return 0;
 }
@@ -308,7 +312,8 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
        int ret;
 
        if (simple_dai->sysclk) {
-               ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 0);
+               ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
+                                            simple_dai->clk_direction);
                if (ret && ret != -ENOTSUPP) {
                        dev_err(dai->dev, "simple-card: set_sysclk error\n");
                        return ret;
index a75b385455c4f62c0f473baea203f94b9c990a8b..48606c63562a7694a83fe7b4d38210908d38ad4e 100644 (file)
@@ -191,6 +191,10 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
        if (!node)
                return -EINVAL;
 
+       ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
+       if (ret < 0)
+               return ret;
+
        ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
        if (ret < 0)
                return ret;
@@ -296,6 +300,7 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
 static struct platform_driver asoc_simple_card = {
        .driver = {
                .name = "simple-scu-audio-card",
+               .pm = &snd_soc_pm_ops,
                .of_match_table = asoc_simple_of_match,
        },
        .probe = asoc_simple_card_probe,
index 977a078eb92f236a005bbc232167ab862b339d07..78a6a360b4a65d83288b5abcc9c298ac954e301d 100644 (file)
@@ -151,7 +151,7 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
        return ret;
 }
 
-static struct snd_soc_dai_ops spdif_in_dai_ops = {
+static const struct snd_soc_dai_ops spdif_in_dai_ops = {
        .shutdown       = spdif_in_shutdown,
        .trigger        = spdif_in_trigger,
        .hw_params      = spdif_in_hw_params,
@@ -216,15 +216,15 @@ static int spdif_in_probe(struct platform_device *pdev)
                return -EINVAL;
 
        host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
-       if (!host) {
-               dev_warn(&pdev->dev, "kzalloc fail\n");
+       if (!host)
                return -ENOMEM;
-       }
 
        host->io_base = io_base;
        host->irq = platform_get_irq(pdev, 0);
-       if (host->irq < 0)
-               return -EINVAL;
+       if (host->irq < 0) {
+               dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq);
+               return host->irq;
+       }
 
        host->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(host->clk))
index 0a72d52d533ee6f8c657dde689231141cbe26505..58d5843811f9421a96fa66ec84b452678bf8ce59 100644 (file)
@@ -282,10 +282,8 @@ static int spdif_out_probe(struct platform_device *pdev)
        int ret;
 
        host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
-       if (!host) {
-               dev_warn(&pdev->dev, "kzalloc fail\n");
+       if (!host)
                return -ENOMEM;
-       }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        host->io_base = devm_ioremap_resource(&pdev->dev, res);
index 8052629a89dfd2d4328b139b218f5e75ca9c33e3..6d0bf78d114d0da74f5b61f5c576c12483d562d9 100644 (file)
@@ -840,7 +840,7 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
        }
 
        /* Reset */
-       rst = devm_reset_control_get(&pdev->dev, NULL);
+       rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
        if (!IS_ERR(rst)) {
                reset_control_assert(rst);
                udelay(2);
index f7713314913b9844ebed680b5179af906101bc2a..1258bef4dcb37e8f8a9e0ea9d69466d64ccd02ed 100644 (file)
@@ -85,7 +85,7 @@ static int stm32_sai_probe(struct platform_device *pdev)
        }
 
        /* reset */
-       rst = reset_control_get(&pdev->dev, NULL);
+       rst = reset_control_get_exclusive(&pdev->dev, NULL);
        if (!IS_ERR(rst)) {
                reset_control_assert(rst);
                udelay(2);
index 4e4250bdb75a83a4d73e6a250fc4edef6f26df1d..84cc5678beba5e9367e981c765cad3045aabe32f 100644 (file)
@@ -930,7 +930,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
                return ret;
        }
 
-       rst = devm_reset_control_get(&pdev->dev, NULL);
+       rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
        if (!IS_ERR(rst)) {
                reset_control_assert(rst);
                udelay(2);
index 150069987c0c3e5fd463636be8e46a832834cb1b..baa9007464ed7458d3c2ba4a777e6f83903989cf 100644 (file)
@@ -762,7 +762,7 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
        { "Mic1", NULL, "VMIC" },
 };
 
-static struct snd_soc_codec_driver sun4i_codec_codec = {
+static const struct snd_soc_codec_driver sun4i_codec_codec = {
        .component_driver = {
                .controls               = sun4i_codec_controls,
                .num_controls           = ARRAY_SIZE(sun4i_codec_controls),
@@ -1068,7 +1068,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
        { "Right ADC", NULL, "Right ADC Mixer" },
 };
 
-static struct snd_soc_codec_driver sun6i_codec_codec = {
+static const struct snd_soc_codec_driver sun6i_codec_codec = {
        .component_driver = {
                .controls               = sun6i_codec_codec_widgets,
                .num_controls           = ARRAY_SIZE(sun6i_codec_codec_widgets),
@@ -1096,7 +1096,7 @@ static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = {
 
 };
 
-static struct snd_soc_codec_driver sun8i_a23_codec_codec = {
+static const struct snd_soc_codec_driver sun8i_a23_codec_codec = {
        .component_driver = {
                .controls               = sun8i_a23_codec_codec_controls,
                .num_controls           = ARRAY_SIZE(sun8i_a23_codec_codec_controls),
@@ -1171,9 +1171,8 @@ static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
 {
        struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
 
-       if (scodec->gpio_pa)
-               gpiod_set_value_cansleep(scodec->gpio_pa,
-                                        !!SND_SOC_DAPM_EVENT_ON(event));
+       gpiod_set_value_cansleep(scodec->gpio_pa,
+                                !!SND_SOC_DAPM_EVENT_ON(event));
 
        return 0;
 }
@@ -1574,7 +1573,8 @@ static int sun4i_codec_probe(struct platform_device *pdev)
        }
 
        if (quirks->has_reset) {
-               scodec->rst = devm_reset_control_get(&pdev->dev, NULL);
+               scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
+                                                              NULL);
                if (IS_ERR(scodec->rst)) {
                        dev_err(&pdev->dev, "Failed to get reset control\n");
                        return PTR_ERR(scodec->rst);
@@ -1655,7 +1655,6 @@ static int sun4i_codec_probe(struct platform_device *pdev)
                goto err_unregister_codec;
        }
 
-       platform_set_drvdata(pdev, card);
        snd_soc_card_set_drvdata(card, scodec);
 
        ret = snd_soc_register_card(card);
index 3635bbc72cbcd3f4053569195007f57762758e7f..04f92583a9696569aeac2a92084643a94cbe57d6 100644 (file)
@@ -50,6 +50,8 @@
 #define SUN4I_I2S_FMT0_FMT_RIGHT_J                     (2 << 0)
 #define SUN4I_I2S_FMT0_FMT_LEFT_J                      (1 << 0)
 #define SUN4I_I2S_FMT0_FMT_I2S                         (0 << 0)
+#define SUN4I_I2S_FMT0_POLARITY_INVERTED               (1)
+#define SUN4I_I2S_FMT0_POLARITY_NORMAL                 (0)
 
 #define SUN4I_I2S_FMT1_REG             0x08
 #define SUN4I_I2S_FIFO_TX_REG          0x0c
@@ -82,7 +84,7 @@
 #define SUN4I_I2S_TX_CNT_REG           0x2c
 
 #define SUN4I_I2S_TX_CHAN_SEL_REG      0x30
-#define SUN4I_I2S_TX_CHAN_SEL(num_chan)                (((num_chan) - 1) << 0)
+#define SUN4I_I2S_CHAN_SEL(num_chan)           (((num_chan) - 1) << 0)
 
 #define SUN4I_I2S_TX_CHAN_MAP_REG      0x34
 #define SUN4I_I2S_TX_CHAN_MAP(chan, sample)    ((sample) << (chan << 2))
 #define SUN4I_I2S_RX_CHAN_SEL_REG      0x38
 #define SUN4I_I2S_RX_CHAN_MAP_REG      0x3c
 
+/* Defines required for sun8i-h3 support */
+#define SUN8I_I2S_CTRL_BCLK_OUT                        BIT(18)
+#define SUN8I_I2S_CTRL_LRCK_OUT                        BIT(17)
+
+#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK                GENMASK(17, 8)
+#define SUN8I_I2S_FMT0_LRCK_PERIOD(period)     ((period - 1) << 8)
+
+#define SUN8I_I2S_INT_STA_REG          0x0c
+#define SUN8I_I2S_FIFO_TX_REG          0x20
+
+#define SUN8I_I2S_CHAN_CFG_REG         0x30
+#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK    GENMASK(6, 4)
+#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan)   (chan - 1)
+#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK    GENMASK(2, 0)
+#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan)   (chan - 1)
+
+#define SUN8I_I2S_TX_CHAN_MAP_REG      0x44
+#define SUN8I_I2S_TX_CHAN_SEL_REG      0x34
+#define SUN8I_I2S_TX_CHAN_OFFSET_MASK          GENMASK(13, 11)
+#define SUN8I_I2S_TX_CHAN_OFFSET(offset)       (offset << 12)
+#define SUN8I_I2S_TX_CHAN_EN_MASK              GENMASK(11, 4)
+#define SUN8I_I2S_TX_CHAN_EN(num_chan)         (((1 << num_chan) - 1) << 4)
+
+#define SUN8I_I2S_RX_CHAN_SEL_REG      0x54
+#define SUN8I_I2S_RX_CHAN_MAP_REG      0x58
+
+/**
+ * struct sun4i_i2s_quirks - Differences between SoC variants.
+ *
+ * @has_reset: SoC needs reset deasserted.
+ * @has_slave_select_bit: SoC has a bit to enable slave mode.
+ * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
+ * @has_chcfg: tx and rx slot number need to be set.
+ * @has_chsel_tx_chen: SoC requires that the tx channels are enabled.
+ * @has_chsel_offset: SoC uses offset for selecting dai operational mode.
+ * @reg_offset_txdata: offset of the tx fifo.
+ * @sun4i_i2s_regmap: regmap config to use.
+ * @mclk_offset: Value by which mclkdiv needs to be adjusted.
+ * @bclk_offset: Value by which bclkdiv needs to be adjusted.
+ * @fmt_offset: Value by which wss and sr needs to be adjusted.
+ * @field_clkdiv_mclk_en: regmap field to enable mclk output.
+ * @field_fmt_wss: regmap field to set word select size.
+ * @field_fmt_sr: regmap field to set sample resolution.
+ * @field_fmt_bclk: regmap field to set clk polarity.
+ * @field_fmt_lrclk: regmap field to set frame polarity.
+ * @field_fmt_mode: regmap field to set the operational mode.
+ * @field_txchanmap: location of the tx channel mapping register.
+ * @field_rxchanmap: location of the rx channel mapping register.
+ * @field_txchansel: location of the tx channel select bit fields.
+ * @field_rxchansel: location of the rx channel select bit fields.
+ */
+struct sun4i_i2s_quirks {
+       bool                            has_reset;
+       bool                            has_slave_select_bit;
+       bool                            has_fmt_set_lrck_period;
+       bool                            has_chcfg;
+       bool                            has_chsel_tx_chen;
+       bool                            has_chsel_offset;
+       unsigned int                    reg_offset_txdata;      /* TX FIFO */
+       const struct regmap_config      *sun4i_i2s_regmap;
+       unsigned int                    mclk_offset;
+       unsigned int                    bclk_offset;
+       unsigned int                    fmt_offset;
+
+       /* Register fields for i2s */
+       struct reg_field                field_clkdiv_mclk_en;
+       struct reg_field                field_fmt_wss;
+       struct reg_field                field_fmt_sr;
+       struct reg_field                field_fmt_bclk;
+       struct reg_field                field_fmt_lrclk;
+       struct reg_field                field_fmt_mode;
+       struct reg_field                field_txchanmap;
+       struct reg_field                field_rxchanmap;
+       struct reg_field                field_txchansel;
+       struct reg_field                field_rxchansel;
+};
+
 struct sun4i_i2s {
        struct clk      *bus_clk;
        struct clk      *mod_clk;
@@ -100,6 +179,20 @@ struct sun4i_i2s {
 
        struct snd_dmaengine_dai_dma_data       capture_dma_data;
        struct snd_dmaengine_dai_dma_data       playback_dma_data;
+
+       /* Register fields for i2s */
+       struct regmap_field     *field_clkdiv_mclk_en;
+       struct regmap_field     *field_fmt_wss;
+       struct regmap_field     *field_fmt_sr;
+       struct regmap_field     *field_fmt_bclk;
+       struct regmap_field     *field_fmt_lrclk;
+       struct regmap_field     *field_fmt_mode;
+       struct regmap_field     *field_txchanmap;
+       struct regmap_field     *field_rxchanmap;
+       struct regmap_field     *field_txchansel;
+       struct regmap_field     *field_rxchansel;
+
+       const struct sun4i_i2s_quirks   *variant;
 };
 
 struct sun4i_i2s_clk_div {
@@ -114,6 +207,7 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
        { .div = 8, .val = 3 },
        { .div = 12, .val = 4 },
        { .div = 16, .val = 5 },
+       /* TODO - extend divide ratio supported by newer SoCs */
 };
 
 static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
@@ -125,6 +219,7 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
        { .div = 12, .val = 5 },
        { .div = 16, .val = 6 },
        { .div = 24, .val = 7 },
+       /* TODO - extend divide ratio supported by newer SoCs */
 };
 
 static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
@@ -226,10 +321,21 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
        if (mclk_div < 0)
                return -EINVAL;
 
+       /* Adjust the clock division values if needed */
+       bclk_div += i2s->variant->bclk_offset;
+       mclk_div += i2s->variant->mclk_offset;
+
        regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
                     SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
-                    SUN4I_I2S_CLK_DIV_MCLK(mclk_div) |
-                    SUN4I_I2S_CLK_DIV_MCLK_EN);
+                    SUN4I_I2S_CLK_DIV_MCLK(mclk_div));
+
+       regmap_field_write(i2s->field_clkdiv_mclk_en, 1);
+
+       /* Set sync period */
+       if (i2s->variant->has_fmt_set_lrck_period)
+               regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+                                  SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
+                                  SUN8I_I2S_FMT0_LRCK_PERIOD(32));
 
        return 0;
 }
@@ -239,12 +345,38 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
        struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-       int sr, wss;
+       int sr, wss, channels;
        u32 width;
 
-       if (params_channels(params) != 2)
+       channels = params_channels(params);
+       if (channels != 2)
                return -EINVAL;
 
+       if (i2s->variant->has_chcfg) {
+               regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+                                  SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
+                                  SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
+               regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+                                  SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
+                                  SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
+       }
+
+       /* Map the channels for playback and capture */
+       regmap_field_write(i2s->field_txchanmap, 0x76543210);
+       regmap_field_write(i2s->field_rxchanmap, 0x00003210);
+
+       /* Configure the channels */
+       regmap_field_write(i2s->field_txchansel,
+                          SUN4I_I2S_CHAN_SEL(params_channels(params)));
+
+       regmap_field_write(i2s->field_rxchansel,
+                          SUN4I_I2S_CHAN_SEL(params_channels(params)));
+
+       if (i2s->variant->has_chsel_tx_chen)
+               regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+                                  SUN8I_I2S_TX_CHAN_EN_MASK,
+                                  SUN8I_I2S_TX_CHAN_EN(channels));
+
        switch (params_physical_width(params)) {
        case 16:
                width = DMA_SLAVE_BUSWIDTH_2_BYTES;
@@ -264,9 +396,10 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
-                          SUN4I_I2S_FMT0_WSS_MASK | SUN4I_I2S_FMT0_SR_MASK,
-                          SUN4I_I2S_FMT0_WSS(wss) | SUN4I_I2S_FMT0_SR(sr));
+       regmap_field_write(i2s->field_fmt_wss,
+                          wss + i2s->variant->fmt_offset);
+       regmap_field_write(i2s->field_fmt_sr,
+                          sr + i2s->variant->fmt_offset);
 
        return sun4i_i2s_set_clk_rate(i2s, params_rate(params),
                                      params_width(params));
@@ -276,11 +409,15 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
        u32 val;
+       u32 offset = 0;
+       u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
+       u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
 
        /* DAI Mode */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                val = SUN4I_I2S_FMT0_FMT_I2S;
+               offset = 1;
                break;
        case SND_SOC_DAIFMT_LEFT_J:
                val = SUN4I_I2S_FMT0_FMT_LEFT_J;
@@ -292,59 +429,89 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                return -EINVAL;
        }
 
-       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
-                          SUN4I_I2S_FMT0_FMT_MASK,
-                          val);
+       if (i2s->variant->has_chsel_offset) {
+               /*
+                * offset being set indicates that we're connected to an i2s
+                * device, however offset is only used on the sun8i block and
+                * i2s shares the same setting with the LJ format. Increment
+                * val so that the bit to value to write is correct.
+                */
+               if (offset > 0)
+                       val++;
+               /* blck offset determines whether i2s or LJ */
+               regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+                                  SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+                                  SUN8I_I2S_TX_CHAN_OFFSET(offset));
+       }
+
+       regmap_field_write(i2s->field_fmt_mode, val);
 
        /* DAI clock polarity */
        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
        case SND_SOC_DAIFMT_IB_IF:
                /* Invert both clocks */
-               val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
-                       SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
+               bclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
+               lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
                break;
        case SND_SOC_DAIFMT_IB_NF:
                /* Invert bit clock */
-               val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
-                       SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
+               bclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
                break;
        case SND_SOC_DAIFMT_NB_IF:
                /* Invert frame clock */
-               val = SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED |
-                       SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL;
+               lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
                break;
        case SND_SOC_DAIFMT_NB_NF:
-               /* Nothing to do for both normal cases */
-               val = SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL |
-                       SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
                break;
        default:
                return -EINVAL;
        }
 
-       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
-                          SUN4I_I2S_FMT0_BCLK_POLARITY_MASK |
-                          SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK,
-                          val);
-
-       /* DAI clock master masks */
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBS_CFS:
-               /* BCLK and LRCLK master */
-               val = SUN4I_I2S_CTRL_MODE_MASTER;
-               break;
-       case SND_SOC_DAIFMT_CBM_CFM:
-               /* BCLK and LRCLK slave */
-               val = SUN4I_I2S_CTRL_MODE_SLAVE;
-               break;
-       default:
-               return -EINVAL;
+       regmap_field_write(i2s->field_fmt_bclk, bclk_polarity);
+       regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity);
+
+       if (i2s->variant->has_slave_select_bit) {
+               /* DAI clock master masks */
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* BCLK and LRCLK master */
+                       val = SUN4I_I2S_CTRL_MODE_MASTER;
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* BCLK and LRCLK slave */
+                       val = SUN4I_I2S_CTRL_MODE_SLAVE;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                                  SUN4I_I2S_CTRL_MODE_MASK,
+                                  val);
+       } else {
+               /*
+                * The newer i2s block does not have a slave select bit,
+                * instead the clk pins are configured as inputs.
+                */
+               /* DAI clock master masks */
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* BCLK and LRCLK master */
+                       val = SUN8I_I2S_CTRL_BCLK_OUT |
+                               SUN8I_I2S_CTRL_LRCK_OUT;
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* BCLK and LRCLK slave */
+                       val = 0;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                                  SUN8I_I2S_CTRL_BCLK_OUT |
+                                  SUN8I_I2S_CTRL_LRCK_OUT,
+                                  val);
        }
 
-       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
-                          SUN4I_I2S_CTRL_MODE_MASK,
-                          val);
-
        /* Set significant bits in our FIFOs */
        regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
                           SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK |
@@ -459,21 +626,14 @@ static int sun4i_i2s_startup(struct snd_pcm_substream *substream,
        struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
 
        /* Enable the whole hardware block */
-       regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG,
-                    SUN4I_I2S_CTRL_GL_EN);
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_GL_EN, SUN4I_I2S_CTRL_GL_EN);
 
        /* Enable the first output line */
        regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
                           SUN4I_I2S_CTRL_SDO_EN_MASK,
                           SUN4I_I2S_CTRL_SDO_EN(0));
 
-       /* Enable the first two channels */
-       regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
-                    SUN4I_I2S_TX_CHAN_SEL(2));
-
-       /* Map them to the two first samples coming in */
-       regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG,
-                    SUN4I_I2S_TX_CHAN_MAP(0, 0) | SUN4I_I2S_TX_CHAN_MAP(1, 1));
 
        return clk_prepare_enable(i2s->mod_clk);
 }
@@ -490,7 +650,8 @@ static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream,
                           SUN4I_I2S_CTRL_SDO_EN_MASK, 0);
 
        /* Disable the whole hardware block */
-       regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0);
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_GL_EN, 0);
 }
 
 static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -589,6 +750,27 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SUN8I_I2S_FIFO_TX_REG:
+               return false;
+
+       default:
+               return true;
+       }
+}
+
+static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+       if (reg == SUN8I_I2S_INT_STA_REG)
+               return true;
+       if (reg == SUN8I_I2S_FIFO_TX_REG)
+               return false;
+
+       return sun4i_i2s_volatile_reg(dev, reg);
+}
+
 static const struct reg_default sun4i_i2s_reg_defaults[] = {
        { SUN4I_I2S_CTRL_REG, 0x00000000 },
        { SUN4I_I2S_FMT0_REG, 0x0000000c },
@@ -602,6 +784,20 @@ static const struct reg_default sun4i_i2s_reg_defaults[] = {
        { SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 },
 };
 
+static const struct reg_default sun8i_i2s_reg_defaults[] = {
+       { SUN4I_I2S_CTRL_REG, 0x00060000 },
+       { SUN4I_I2S_FMT0_REG, 0x00000033 },
+       { SUN4I_I2S_FMT1_REG, 0x00000030 },
+       { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
+       { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
+       { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
+       { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
+       { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
+       { SUN8I_I2S_TX_CHAN_MAP_REG, 0x00000000 },
+       { SUN8I_I2S_RX_CHAN_SEL_REG, 0x00000000 },
+       { SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
+};
+
 static const struct regmap_config sun4i_i2s_regmap_config = {
        .reg_bits       = 32,
        .reg_stride     = 4,
@@ -616,6 +812,19 @@ static const struct regmap_config sun4i_i2s_regmap_config = {
        .volatile_reg   = sun4i_i2s_volatile_reg,
 };
 
+static const struct regmap_config sun8i_i2s_regmap_config = {
+       .reg_bits       = 32,
+       .reg_stride     = 4,
+       .val_bits       = 32,
+       .max_register   = SUN8I_I2S_RX_CHAN_MAP_REG,
+       .cache_type     = REGCACHE_FLAT,
+       .reg_defaults   = sun8i_i2s_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(sun8i_i2s_reg_defaults),
+       .writeable_reg  = sun4i_i2s_wr_reg,
+       .readable_reg   = sun8i_i2s_rd_reg,
+       .volatile_reg   = sun8i_i2s_volatile_reg,
+};
+
 static int sun4i_i2s_runtime_resume(struct device *dev)
 {
        struct sun4i_i2s *i2s = dev_get_drvdata(dev);
@@ -654,22 +863,129 @@ static int sun4i_i2s_runtime_suspend(struct device *dev)
        return 0;
 }
 
-struct sun4i_i2s_quirks {
-       bool has_reset;
-};
-
 static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
-       .has_reset      = false,
+       .has_reset              = false,
+       .reg_offset_txdata      = SUN4I_I2S_FIFO_TX_REG,
+       .sun4i_i2s_regmap       = &sun4i_i2s_regmap_config,
+       .field_clkdiv_mclk_en   = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
+       .field_fmt_wss          = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
+       .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
+       .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
+       .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .has_slave_select_bit   = true,
+       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
+       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
+       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
+       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
+       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
 };
 
 static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
-       .has_reset      = true,
+       .has_reset              = true,
+       .reg_offset_txdata      = SUN4I_I2S_FIFO_TX_REG,
+       .sun4i_i2s_regmap       = &sun4i_i2s_regmap_config,
+       .field_clkdiv_mclk_en   = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
+       .field_fmt_wss          = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
+       .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
+       .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
+       .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .has_slave_select_bit   = true,
+       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
+       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
+       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
+       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
+       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
+};
+
+static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
+       .has_reset              = true,
+       .reg_offset_txdata      = SUN8I_I2S_FIFO_TX_REG,
+       .sun4i_i2s_regmap       = &sun8i_i2s_regmap_config,
+       .mclk_offset            = 1,
+       .bclk_offset            = 2,
+       .fmt_offset             = 3,
+       .has_fmt_set_lrck_period = true,
+       .has_chcfg              = true,
+       .has_chsel_tx_chen      = true,
+       .has_chsel_offset       = true,
+       .field_clkdiv_mclk_en   = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
+       .field_fmt_wss          = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
+       .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
+       .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
+       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
+       .field_txchanmap        = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
+       .field_rxchanmap        = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
+       .field_txchansel        = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
+       .field_rxchansel        = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
 };
 
+static int sun4i_i2s_init_regmap_fields(struct device *dev,
+                                       struct sun4i_i2s *i2s)
+{
+       i2s->field_clkdiv_mclk_en =
+               devm_regmap_field_alloc(dev, i2s->regmap,
+                                       i2s->variant->field_clkdiv_mclk_en);
+       if (IS_ERR(i2s->field_clkdiv_mclk_en))
+               return PTR_ERR(i2s->field_clkdiv_mclk_en);
+
+       i2s->field_fmt_wss =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_fmt_wss);
+       if (IS_ERR(i2s->field_fmt_wss))
+               return PTR_ERR(i2s->field_fmt_wss);
+
+       i2s->field_fmt_sr =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_fmt_sr);
+       if (IS_ERR(i2s->field_fmt_sr))
+               return PTR_ERR(i2s->field_fmt_sr);
+
+       i2s->field_fmt_bclk =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_fmt_bclk);
+       if (IS_ERR(i2s->field_fmt_bclk))
+               return PTR_ERR(i2s->field_fmt_bclk);
+
+       i2s->field_fmt_lrclk =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_fmt_lrclk);
+       if (IS_ERR(i2s->field_fmt_lrclk))
+               return PTR_ERR(i2s->field_fmt_lrclk);
+
+       i2s->field_fmt_mode =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_fmt_mode);
+       if (IS_ERR(i2s->field_fmt_mode))
+               return PTR_ERR(i2s->field_fmt_mode);
+
+       i2s->field_txchanmap =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_txchanmap);
+       if (IS_ERR(i2s->field_txchanmap))
+               return PTR_ERR(i2s->field_txchanmap);
+
+       i2s->field_rxchanmap =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_rxchanmap);
+       if (IS_ERR(i2s->field_rxchanmap))
+               return PTR_ERR(i2s->field_rxchanmap);
+
+       i2s->field_txchansel =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_txchansel);
+       if (IS_ERR(i2s->field_txchansel))
+               return PTR_ERR(i2s->field_txchansel);
+
+       i2s->field_rxchansel =
+                       devm_regmap_field_alloc(dev, i2s->regmap,
+                                               i2s->variant->field_rxchansel);
+       return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
+}
+
 static int sun4i_i2s_probe(struct platform_device *pdev)
 {
        struct sun4i_i2s *i2s;
-       const struct sun4i_i2s_quirks *quirks;
        struct resource *res;
        void __iomem *regs;
        int irq, ret;
@@ -690,8 +1006,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
                return irq;
        }
 
-       quirks = of_device_get_match_data(&pdev->dev);
-       if (!quirks) {
+       i2s->variant = of_device_get_match_data(&pdev->dev);
+       if (!i2s->variant) {
                dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
                return -ENODEV;
        }
@@ -703,7 +1019,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
        }
 
        i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
-                                           &sun4i_i2s_regmap_config);
+                                           i2s->variant->sun4i_i2s_regmap);
        if (IS_ERR(i2s->regmap)) {
                dev_err(&pdev->dev, "Regmap initialisation failed\n");
                return PTR_ERR(i2s->regmap);
@@ -715,8 +1031,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
                return PTR_ERR(i2s->mod_clk);
        }
 
-       if (quirks->has_reset) {
-               i2s->rst = devm_reset_control_get(&pdev->dev, NULL);
+       if (i2s->variant->has_reset) {
+               i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
                if (IS_ERR(i2s->rst)) {
                        dev_err(&pdev->dev, "Failed to get reset control\n");
                        return PTR_ERR(i2s->rst);
@@ -732,7 +1048,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
                }
        }
 
-       i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
+       i2s->playback_dma_data.addr = res->start +
+                                       i2s->variant->reg_offset_txdata;
        i2s->playback_dma_data.maxburst = 8;
 
        i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
@@ -759,6 +1076,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
                goto err_suspend;
        }
 
+       ret = sun4i_i2s_init_regmap_fields(&pdev->dev, i2s);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not initialise regmap fields\n");
+               goto err_suspend;
+       }
+
        return 0;
 
 err_suspend:
@@ -797,6 +1120,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
                .compatible = "allwinner,sun6i-a31-i2s",
                .data = &sun6i_a31_i2s_quirks,
        },
+       {
+               .compatible = "allwinner,sun8i-h3-i2s",
+               .data = &sun8i_h3_i2s_quirks,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
index eaefd07a5ed080ecbe9373378a767cfffd6faf68..b4af4aabead1339887e69d2acdd20ee342c58171 100644 (file)
@@ -458,11 +458,16 @@ static int sun4i_spdif_runtime_suspend(struct device *dev)
 static int sun4i_spdif_runtime_resume(struct device *dev)
 {
        struct sun4i_spdif_dev *host  = dev_get_drvdata(dev);
+       int ret;
 
-       clk_prepare_enable(host->spdif_clk);
-       clk_prepare_enable(host->apb_clk);
+       ret = clk_prepare_enable(host->spdif_clk);
+       if (ret)
+               return ret;
+       ret = clk_prepare_enable(host->apb_clk);
+       if (ret)
+               clk_disable_unprepare(host->spdif_clk);
 
-       return 0;
+       return ret;
 }
 
 static int sun4i_spdif_probe(struct platform_device *pdev)
@@ -520,7 +525,8 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, host);
 
        if (quirks->has_reset) {
-               host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
+               host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
+                                                                     NULL);
                if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
                        ret = -EPROBE_DEFER;
                        dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
index 5723c3404f6bd6a896aed90b8b37d143de26c075..abfb710df7cbceb54c9fc60ab7502f088d630874 100644 (file)
@@ -341,7 +341,7 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
          "AIF1 Slot 0 Right"},
 };
 
-static struct snd_soc_dai_ops sun8i_codec_dai_ops = {
+static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
        .hw_params = sun8i_codec_hw_params,
        .set_fmt = sun8i_set_fmt,
 };
@@ -360,7 +360,7 @@ static struct snd_soc_dai_driver sun8i_codec_dai = {
        .ops = &sun8i_codec_dai_ops,
 };
 
-static struct snd_soc_codec_driver sun8i_soc_codec = {
+static const struct snd_soc_codec_driver sun8i_soc_codec = {
        .component_driver = {
                .dapm_widgets           = sun8i_codec_dapm_widgets,
                .num_dapm_widgets       = ARRAY_SIZE(sun8i_codec_dapm_widgets),