ASoC: rockchip: i2s: configure the sdio pins' iomux mode
authorSugar Zhang <sugar.zhang@rock-chips.com>
Mon, 11 Apr 2016 09:26:03 +0000 (17:26 +0800)
committerMark Brown <broonie@kernel.org>
Mon, 30 May 2016 15:19:41 +0000 (16:19 +0100)
There are 3 i2s sdio pins, which iomux mode is as follows:

 - sdi3_sdo1
 - sdi2_sdo2
 - sdi1_sdo3

we need to configure these pins' iomux mode via the GRF register
when use multi channel playback/capture.

Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/sound/rockchip-i2s.txt
sound/soc/rockchip/rockchip_i2s.c
sound/soc/rockchip/rockchip_i2s.h

index 6e86d8aa29b4639397f51a8e73b8d8ede79c28fe..4ea29aa9af59a86d34577329835b8437e72bd86e 100644 (file)
@@ -23,6 +23,11 @@ Required properties:
 - rockchip,playback-channels: max playback channels, if not set, 8 channels default.
 - rockchip,capture-channels: max capture channels, if not set, 2 channels default.
 
+Required properties for controller which support multi channels
+playback/capture:
+
+- rockchip,grf: the phandle of the syscon node for GRF register.
+
 Example for rk3288 I2S controller:
 
 i2s@ff890000 {
index 574c6af28c068e164601794ec102c87b33ba40e1..2f290f3a0ac7b3c74698f01c645512c250a57518 100644 (file)
  */
 
 #include <linux/module.h>
+#include <linux/mfd/syscon.h>
 #include <linux/delay.h>
 #include <linux/of_gpio.h>
+#include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 
 #define DRV_NAME "rockchip-i2s"
 
+struct rk_i2s_pins {
+       u32 reg_offset;
+       u32 shift;
+};
+
 struct rk_i2s_dev {
        struct device *dev;
 
@@ -33,6 +40,7 @@ struct rk_i2s_dev {
        struct snd_dmaengine_dai_dma_data playback_dma_data;
 
        struct regmap *regmap;
+       struct regmap *grf;
 
 /*
  * Used to indicate the tx/rx status.
@@ -42,6 +50,7 @@ struct rk_i2s_dev {
        bool tx_start;
        bool rx_start;
        bool is_master_mode;
+       const struct rk_i2s_pins *pins;
 };
 
 static int i2s_runtime_suspend(struct device *dev)
@@ -300,6 +309,30 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
                                   I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
                                   val);
 
+       if (!IS_ERR(i2s->grf) && i2s->pins) {
+               regmap_read(i2s->regmap, I2S_TXCR, &val);
+               val &= I2S_TXCR_CSR_MASK;
+
+               switch (val) {
+               case I2S_CHN_4:
+                       val = I2S_IO_4CH_OUT_6CH_IN;
+                       break;
+               case I2S_CHN_6:
+                       val = I2S_IO_6CH_OUT_4CH_IN;
+                       break;
+               case I2S_CHN_8:
+                       val = I2S_IO_8CH_OUT_2CH_IN;
+                       break;
+               default:
+                       val = I2S_IO_2CH_OUT_8CH_IN;
+                       break;
+               }
+
+               val <<= i2s->pins->shift;
+               val |= (I2S_IO_DIRECTION_MASK << i2s->pins->shift) << 16;
+               regmap_write(i2s->grf, i2s->pins->reg_offset, val);
+       }
+
        regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
                           I2S_DMACR_TDL(16));
        regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
@@ -485,9 +518,23 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
        .cache_type = REGCACHE_FLAT,
 };
 
+static const struct rk_i2s_pins rk3399_i2s_pins = {
+       .reg_offset = 0xe220,
+       .shift = 11,
+};
+
+static const struct of_device_id rockchip_i2s_match[] = {
+       { .compatible = "rockchip,rk3066-i2s", },
+       { .compatible = "rockchip,rk3188-i2s", },
+       { .compatible = "rockchip,rk3288-i2s", },
+       { .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
+       {},
+};
+
 static int rockchip_i2s_probe(struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
+       const struct of_device_id *of_id;
        struct rk_i2s_dev *i2s;
        struct snd_soc_dai_driver *soc_dai;
        struct resource *res;
@@ -501,6 +548,17 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       i2s->dev = &pdev->dev;
+
+       i2s->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+       if (!IS_ERR(i2s->grf)) {
+               of_id = of_match_device(rockchip_i2s_match, &pdev->dev);
+               if (!of_id || !of_id->data)
+                       return -EINVAL;
+
+               i2s->pins = of_id->data;
+       }
+
        /* try to prepare related clocks */
        i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
        if (IS_ERR(i2s->hclk)) {
@@ -540,7 +598,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
        i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
        i2s->capture_dma_data.maxburst = 4;
 
-       i2s->dev = &pdev->dev;
        dev_set_drvdata(&pdev->dev, i2s);
 
        pm_runtime_enable(&pdev->dev);
@@ -606,14 +663,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id rockchip_i2s_match[] = {
-       { .compatible = "rockchip,rk3066-i2s", },
-       { .compatible = "rockchip,rk3188-i2s", },
-       { .compatible = "rockchip,rk3288-i2s", },
-       { .compatible = "rockchip,rk3399-i2s", },
-       {},
-};
-
 static const struct dev_pm_ops rockchip_i2s_pm_ops = {
        SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
                           NULL)
index dc6e2c74d08818bf68d333f2ca4d811eb914b7ed..8e239d301bc7cbba1c320cd47c7dc08e7cfefd6c 100644 (file)
@@ -236,4 +236,11 @@ enum {
 #define I2S_TXDR       (0x0024)
 #define I2S_RXDR       (0x0028)
 
+/* io direction cfg register */
+#define I2S_IO_DIRECTION_MASK  (7)
+#define I2S_IO_8CH_OUT_2CH_IN  (0)
+#define I2S_IO_6CH_OUT_4CH_IN  (4)
+#define I2S_IO_4CH_OUT_6CH_IN  (6)
+#define I2S_IO_2CH_OUT_8CH_IN  (7)
+
 #endif /* _ROCKCHIP_IIS_H */