Merge branch 'for-5.0' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorMark Brown <broonie@kernel.org>
Wed, 13 Feb 2019 11:45:08 +0000 (11:45 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 13 Feb 2019 11:45:08 +0000 (11:45 +0000)
1  2 
sound/soc/samsung/i2s.c

diff --combined sound/soc/samsung/i2s.c
index cc983afae735ba4f1281b7d4392b4d6beaafa7c6,d4bde4834ce5f1b581f4febe5fb0341ab609b321..2736209144710730fbfb6370a93b0ad020a61720
@@@ -34,9 -34,6 +34,9 @@@
  
  #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
  
 +#define SAMSUNG_I2S_ID_PRIMARY                1
 +#define SAMSUNG_I2S_ID_SECONDARY      2
 +
  struct samsung_i2s_variant_regs {
        unsigned int    bfs_off;
        unsigned int    rfs_off;
@@@ -82,10 -79,8 +82,10 @@@ struct i2s_dai 
  #define DAI_OPENED    (1 << 0) /* Dai is opened */
  #define DAI_MANAGER   (1 << 1) /* Dai is the manager */
        unsigned mode;
 +
        /* Driver for this DAI */
 -      struct snd_soc_dai_driver i2s_dai_drv;
 +      struct snd_soc_dai_driver *drv;
 +
        /* DMA parameters */
        struct snd_dmaengine_dai_dma_data dma_playback;
        struct snd_dmaengine_dai_dma_data dma_capture;
@@@ -97,6 -92,8 +97,6 @@@
        u32     suspend_i2spsr;
        const struct samsung_i2s_variant_regs *variant_regs;
  
 -      /* Spinlock protecting access to the device's registers */
 -      spinlock_t spinlock;
        spinlock_t *lock;
  
        /* Below fields are only valid if this is the primary FIFO */
  /* Lock for cross i/f checks */
  static DEFINE_SPINLOCK(lock);
  
 -/* If this is the 'overlay' stereo DAI */
 +struct samsung_i2s_priv {
 +      struct platform_device *pdev;
 +      struct platform_device *pdev_sec;
 +
 +      /* Spinlock protecting access to the device's registers */
 +      spinlock_t spinlock;
 +
 +      /* CPU DAIs and their corresponding drivers */
 +      struct i2s_dai *dai;
 +      struct snd_soc_dai_driver *dai_drv;
 +      int num_dais;
 +};
 +
 +struct i2s_dai *samsung_i2s_get_pri_dai(struct device *dev)
 +{
 +      struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
 +
 +      return &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1];
 +}
 +
 +/* Returns true if this is the 'overlay' stereo DAI */
  static inline bool is_secondary(struct i2s_dai *i2s)
  {
 -      return i2s->pri_dai ? true : false;
 +      return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY;
  }
  
  /* If operating in SoC-Slave mode */
@@@ -225,9 -202,7 +225,9 @@@ static inline bool any_active(struct i2
  
  static inline struct i2s_dai *to_info(struct snd_soc_dai *dai)
  {
 -      return snd_soc_dai_get_drvdata(dai);
 +      struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
 +
 +      return &priv->dai[dai->id - 1];
  }
  
  static inline bool is_opened(struct i2s_dai *i2s)
@@@ -629,6 -604,7 +629,7 @@@ static int i2s_set_fmt(struct snd_soc_d
        unsigned int fmt)
  {
        struct i2s_dai *i2s = to_info(dai);
+       struct i2s_dai *other = get_other_dai(i2s);
        int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
        u32 mod, tmp = 0;
        unsigned long flags;
                 * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
                 * clock configuration assigned in DT is not overwritten.
                 */
-               if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL)
+               if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL &&
+                   other->clk_data.clks == NULL)
                        i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
                                                        0, SND_SOC_CLOCK_IN);
                break;
@@@ -724,7 -701,9 +726,9 @@@ static int i2s_hw_params(struct snd_pcm
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
  {
        struct i2s_dai *i2s = to_info(dai);
+       struct i2s_dai *other = get_other_dai(i2s);
        u32 mod, mask = 0, val = 0;
+       struct clk *rclksrc;
        unsigned long flags;
  
        WARN_ON(!pm_runtime_active(dai->dev));
  
        i2s->frmclk = params_rate(params);
  
+       rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
+       if (!rclksrc || IS_ERR(rclksrc))
+               rclksrc = other->clk_table[CLK_I2S_RCLK_SRC];
+       if (rclksrc && !IS_ERR(rclksrc))
+               i2s->rclk_srcrate = clk_get_rate(rclksrc);
        return 0;
  }
  
@@@ -911,11 -897,6 +922,6 @@@ static int config_setup(struct i2s_dai 
                return 0;
  
        if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
-               struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
-               if (rclksrc && !IS_ERR(rclksrc))
-                       i2s->rclk_srcrate = clk_get_rate(rclksrc);
                psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
                writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
                dev_dbg(&i2s->pdev->dev,
@@@ -1084,7 -1065,7 +1090,7 @@@ static int samsung_i2s_dai_probe(struc
  
  static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
  {
 -      struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
 +      struct i2s_dai *i2s = to_info(dai);
        unsigned long flags;
  
        pm_runtime_get_sync(dai->dev);
@@@ -1121,63 -1102,47 +1127,63 @@@ static const struct snd_soc_component_d
                                        SNDRV_PCM_FMTBIT_S16_LE | \
                                        SNDRV_PCM_FMTBIT_S24_LE)
  
 -static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev,
 -                              const struct samsung_i2s_dai_data *i2s_dai_data,
 -                              bool sec)
 +static int i2s_alloc_dais(struct samsung_i2s_priv *priv,
 +                        const struct samsung_i2s_dai_data *i2s_dai_data,
 +                        int num_dais)
  {
 -      struct i2s_dai *i2s;
 -
 -      i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
 -      if (i2s == NULL)
 -              return NULL;
 -
 -      i2s->pdev = pdev;
 -      i2s->pri_dai = NULL;
 -      i2s->sec_dai = NULL;
 -      i2s->i2s_dai_drv.id = 1;
 -      i2s->i2s_dai_drv.symmetric_rates = 1;
 -      i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe;
 -      i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove;
 -      i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops;
 -      i2s->i2s_dai_drv.suspend = i2s_suspend;
 -      i2s->i2s_dai_drv.resume = i2s_resume;
 -      i2s->i2s_dai_drv.playback.channels_min = 1;
 -      i2s->i2s_dai_drv.playback.channels_max = 2;
 -      i2s->i2s_dai_drv.playback.rates = i2s_dai_data->pcm_rates;
 -      i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS;
 -
 -      if (!sec) {
 -              i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI;
 -              i2s->i2s_dai_drv.capture.channels_min = 1;
 -              i2s->i2s_dai_drv.capture.channels_max = 2;
 -              i2s->i2s_dai_drv.capture.rates = i2s_dai_data->pcm_rates;
 -              i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
 -      } else {
 -              i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI_SEC;
 +      static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" };
 +      struct snd_soc_dai_driver *dai_drv;
 +      struct i2s_dai *dai;
 +      int i;
 +
 +      priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais,
 +                                   sizeof(*dai), GFP_KERNEL);
 +      if (!priv->dai)
 +              return -ENOMEM;
 +
 +      priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais,
 +                                   sizeof(*dai_drv), GFP_KERNEL);
 +      if (!priv->dai_drv)
 +              return -ENOMEM;
 +
 +      for (i = 0; i < num_dais; i++) {
 +              dai_drv = &priv->dai_drv[i];
 +
 +              dai_drv->probe = samsung_i2s_dai_probe;
 +              dai_drv->remove = samsung_i2s_dai_remove;
 +              dai_drv->suspend = i2s_suspend;
 +              dai_drv->resume = i2s_resume;
 +
 +              dai_drv->symmetric_rates = 1;
 +              dai_drv->ops = &samsung_i2s_dai_ops;
 +
 +              dai_drv->playback.channels_min = 1;
 +              dai_drv->playback.channels_max = 2;
 +              dai_drv->playback.rates = i2s_dai_data->pcm_rates;
 +              dai_drv->playback.formats = SAMSUNG_I2S_FMTS;
 +
 +              dai_drv->id = i + 1;
 +              dai_drv->name = dai_names[i];
 +
 +              priv->dai[i].drv = &priv->dai_drv[i];
 +              priv->dai[i].pdev = priv->pdev;
        }
 -      return i2s;
 +
 +      /* Initialize capture only for the primary DAI */
 +      dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1];
 +
 +      dai_drv->capture.channels_min = 1;
 +      dai_drv->capture.channels_max = 2;
 +      dai_drv->capture.rates = i2s_dai_data->pcm_rates;
 +      dai_drv->capture.formats = SAMSUNG_I2S_FMTS;
 +
 +      return 0;
  }
  
  #ifdef CONFIG_PM
  static int i2s_runtime_suspend(struct device *dev)
  {
 -      struct i2s_dai *i2s = dev_get_drvdata(dev);
 +      struct i2s_dai *i2s = samsung_i2s_get_pri_dai(dev);
  
        i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
        i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
  
  static int i2s_runtime_resume(struct device *dev)
  {
 -      struct i2s_dai *i2s = dev_get_drvdata(dev);
 +      struct i2s_dai *i2s = samsung_i2s_get_pri_dai(dev);
        int ret;
  
        ret = clk_prepare_enable(i2s->clk);
@@@ -1227,7 -1192,7 +1233,7 @@@ static void i2s_unregister_clocks(struc
  
  static void i2s_unregister_clock_provider(struct platform_device *pdev)
  {
 -      struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev);
 +      struct i2s_dai *i2s = samsung_i2s_get_pri_dai(&pdev->dev);
  
        of_clk_del_provider(pdev->dev.of_node);
        i2s_unregister_clocks(i2s);
  
  static int i2s_register_clock_provider(struct platform_device *pdev)
  {
 +
        const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" };
        const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" };
        const char *p_names[2] = { NULL };
        struct device *dev = &pdev->dev;
 -      struct i2s_dai *i2s = dev_get_drvdata(dev);
 +      struct i2s_dai *i2s = samsung_i2s_get_pri_dai(dev);
        const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs;
        const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)];
        struct clk *rclksrc;
        return ret;
  }
  
 +/* Create platform device for the secondary PCM */
 +static int i2s_create_secondary_device(struct samsung_i2s_priv *priv)
 +{
 +      struct platform_device *pdev;
 +      int ret;
 +
 +      pdev = platform_device_register_simple("samsung-i2s-sec", -1, NULL, 0);
 +      if (!pdev)
 +              return -ENOMEM;
 +
 +      ret = device_attach(&pdev->dev);
 +      if (ret < 0) {
 +              dev_info(&pdev->dev, "device_attach() failed\n");
 +              return ret;
 +      }
 +
 +      priv->pdev_sec = pdev;
 +
 +      return 0;
 +}
 +
 +static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv)
 +{
 +      if (priv->pdev_sec) {
 +              platform_device_del(priv->pdev_sec);
 +              priv->pdev_sec = NULL;
 +      }
 +}
  static int samsung_i2s_probe(struct platform_device *pdev)
  {
        struct i2s_dai *pri_dai, *sec_dai = NULL;
        u32 regs_base, quirks = 0, idma_addr = 0;
        struct device_node *np = pdev->dev.of_node;
        const struct samsung_i2s_dai_data *i2s_dai_data;
 -      int ret;
 +      int num_dais, ret;
 +      struct samsung_i2s_priv *priv;
  
        if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
                i2s_dai_data = of_device_get_match_data(&pdev->dev);
                i2s_dai_data = (struct samsung_i2s_dai_data *)
                                platform_get_device_id(pdev)->driver_data;
  
 -      pri_dai = i2s_alloc_dai(pdev, i2s_dai_data, false);
 -      if (!pri_dai) {
 -              dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
 +      /* Nothing to do if it is the secondary device probe */
 +      if (!i2s_dai_data)
 +              return 0;
 +
 +      priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 +      if (!priv)
                return -ENOMEM;
 -      }
  
 -      spin_lock_init(&pri_dai->spinlock);
 -      pri_dai->lock = &pri_dai->spinlock;
 +      quirks = np ? i2s_dai_data->quirks : i2s_pdata->type.quirks;
 +      num_dais = (quirks & QUIRK_SEC_DAI) ? 2 : 1;
 +      priv->pdev = pdev;
 +
 +      ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais);
 +      if (ret < 0)
 +              return ret;
 +
 +      pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1];
 +
 +      spin_lock_init(&priv->spinlock);
 +      pri_dai->lock = &priv->spinlock;
  
        if (!np) {
                if (i2s_pdata == NULL) {
                pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
                pri_dai->filter = i2s_pdata->dma_filter;
  
 -              quirks = i2s_pdata->type.quirks;
                idma_addr = i2s_pdata->type.idma_addr;
        } else {
 -              quirks = i2s_dai_data->quirks;
                if (of_property_read_u32(np, "samsung,idma-addr",
                                         &idma_addr)) {
                        if (quirks & QUIRK_SUPPORTS_IDMA) {
                        }
                }
        }
 -      quirks &= ~(QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA);
  
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        pri_dai->addr = devm_ioremap_resource(&pdev->dev, res);
        pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
  
        if (quirks & QUIRK_PRI_6CHAN)
 -              pri_dai->i2s_dai_drv.playback.channels_max = 6;
 +              pri_dai->drv->playback.channels_max = 6;
  
        ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
 -                                               NULL, NULL);
 -      if (ret < 0)
 -              goto err_disable_clk;
 -
 -      ret = devm_snd_soc_register_component(&pdev->dev,
 -                                      &samsung_i2s_component,
 -                                      &pri_dai->i2s_dai_drv, 1);
 +                                               NULL, NULL, NULL);
        if (ret < 0)
                goto err_disable_clk;
  
        if (quirks & QUIRK_SEC_DAI) {
 -              sec_dai = i2s_alloc_dai(pdev, i2s_dai_data, true);
 -              if (!sec_dai) {
 -                      dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
 -                      ret = -ENOMEM;
 -                      goto err_disable_clk;
 -              }
 +              sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1];
  
 -              sec_dai->lock = &pri_dai->spinlock;
 +              sec_dai->lock = &priv->spinlock;
                sec_dai->variant_regs = pri_dai->variant_regs;
                sec_dai->dma_playback.addr = regs_base + I2STXDS;
                sec_dai->dma_playback.chan_name = "tx-sec";
                sec_dai->pri_dai = pri_dai;
                pri_dai->sec_dai = sec_dai;
  
 -              ret = samsung_asoc_dma_platform_register(&pdev->dev,
 -                                      sec_dai->filter, "tx-sec", NULL);
 +              ret = i2s_create_secondary_device(priv);
                if (ret < 0)
                        goto err_disable_clk;
  
 -              ret = devm_snd_soc_register_component(&pdev->dev,
 -                                              &samsung_i2s_component,
 -                                              &sec_dai->i2s_dai_drv, 1);
 +              ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev,
 +                                              sec_dai->filter, "tx-sec", NULL,
 +                                              &pdev->dev);
                if (ret < 0)
                        goto err_disable_clk;
 +
        }
  
        if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
                goto err_disable_clk;
        }
  
 -      dev_set_drvdata(&pdev->dev, pri_dai);
 +      dev_set_drvdata(&pdev->dev, priv);
 +
 +      ret = devm_snd_soc_register_component(&pdev->dev,
 +                                      &samsung_i2s_component,
 +                                      priv->dai_drv, num_dais);
 +      if (ret < 0)
 +              goto err_disable_clk;
  
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
@@@ -1491,18 -1422,14 +1497,18 @@@ err_disable_pm
        pm_runtime_disable(&pdev->dev);
  err_disable_clk:
        clk_disable_unprepare(pri_dai->clk);
 +      i2s_delete_secondary_device(priv);
        return ret;
  }
  
  static int samsung_i2s_remove(struct platform_device *pdev)
  {
 -      struct i2s_dai *pri_dai;
 +      struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev);
 +      struct i2s_dai *pri_dai = samsung_i2s_get_pri_dai(&pdev->dev);
  
 -      pri_dai = dev_get_drvdata(&pdev->dev);
 +      /* The secondary device has no driver data assigned */
 +      if (!priv)
 +              return 0;
  
        pm_runtime_get_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        i2s_unregister_clock_provider(pdev);
        clk_disable_unprepare(pri_dai->clk);
        pm_runtime_put_noidle(&pdev->dev);
 +      i2s_delete_secondary_device(priv);
  
        return 0;
  }
@@@ -1608,8 -1534,6 +1614,8 @@@ static const struct platform_device_id 
        {
                .name           = "samsung-i2s",
                .driver_data    = (kernel_ulong_t)&i2sv3_dai_type,
 +      }, {
 +              .name           = "samsung-i2s-sec",
        },
        {},
  };