Merge remote-tracking branches 'asoc/topic/tas571x', 'asoc/topic/tlv320aic31xx',...
[linux-2.6-microblaze.git] / sound / soc / rockchip / rockchip_i2s.c
index 574c6af..652e8c5 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,14 +309,38 @@ 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,
                           I2S_DMACR_RDL(16));
 
        val = I2S_CKR_TRCM_TXRX;
-       if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
-               val = I2S_CKR_TRCM_TXSHARE;
+       if (dai->driver->symmetric_rates && rtd->dai_link->symmetric_rates)
+               val = I2S_CKR_TRCM_TXONLY;
 
        regmap_update_bits(i2s->regmap, I2S_CKR,
                           I2S_CKR_TRCM_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)