ASoC: stm32: sai: fix noderef.cocci warnings
[linux-2.6-block.git] / sound / soc / stm / stm32_sai_sub.c
index 56a227e0bd711eb065660f75811ffc682e916359..211589b0b2ef5400276d689599134bd02919691d 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of_irq.h>
@@ -68,6 +69,8 @@
 #define SAI_IEC60958_BLOCK_FRAMES      192
 #define SAI_IEC60958_STATUS_BYTES      24
 
+#define SAI_MCLK_NAME_LEN              32
+
 /**
  * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
  * @pdev: device data pointer
@@ -80,6 +83,7 @@
  * @pdata: SAI block parent data pointer
  * @np_sync_provider: synchronization provider node
  * @sai_ck: kernel clock feeding the SAI clock generator
+ * @sai_mclk: master clock from SAI mclk provider
  * @phys_addr: SAI registers physical base address
  * @mclk_rate: SAI block master clock frequency (Hz). set at init
  * @id: SAI sub block id corresponding to sub-block A or B
@@ -110,6 +114,7 @@ struct stm32_sai_sub_data {
        struct stm32_sai_data *pdata;
        struct device_node *np_sync_provider;
        struct clk *sai_ck;
+       struct clk *sai_mclk;
        dma_addr_t phys_addr;
        unsigned int mclk_rate;
        unsigned int id;
@@ -251,6 +256,176 @@ static const struct snd_kcontrol_new iec958_ctls = {
        .put = snd_pcm_iec958_put,
 };
 
+struct stm32_sai_mclk_data {
+       struct clk_hw hw;
+       unsigned long freq;
+       struct stm32_sai_sub_data *sai_data;
+};
+
+#define to_mclk_data(_hw) container_of(_hw, struct stm32_sai_mclk_data, hw)
+#define STM32_SAI_MAX_CLKS 1
+
+static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai,
+                                unsigned long input_rate,
+                                unsigned long output_rate)
+{
+       int version = sai->pdata->conf->version;
+       int div;
+
+       div = DIV_ROUND_CLOSEST(input_rate, output_rate);
+       if (div > SAI_XCR1_MCKDIV_MAX(version)) {
+               dev_err(&sai->pdev->dev, "Divider %d out of range\n", div);
+               return -EINVAL;
+       }
+       dev_dbg(&sai->pdev->dev, "SAI divider %d\n", div);
+
+       if (input_rate % div)
+               dev_dbg(&sai->pdev->dev,
+                       "Rate not accurate. requested (%ld), actual (%ld)\n",
+                       output_rate, input_rate / div);
+
+       return div;
+}
+
+static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai,
+                                unsigned int div)
+{
+       int version = sai->pdata->conf->version;
+       int ret, cr1, mask;
+
+       if (div > SAI_XCR1_MCKDIV_MAX(version)) {
+               dev_err(&sai->pdev->dev, "Divider %d out of range\n", div);
+               return -EINVAL;
+       }
+
+       mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
+       cr1 = SAI_XCR1_MCKDIV_SET(div);
+       ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
+       if (ret < 0)
+               dev_err(&sai->pdev->dev, "Failed to update CR1 register\n");
+
+       return ret;
+}
+
+static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                     unsigned long *prate)
+{
+       struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
+       struct stm32_sai_sub_data *sai = mclk->sai_data;
+       int div;
+
+       div = stm32_sai_get_clk_div(sai, *prate, rate);
+       if (div < 0)
+               return div;
+
+       mclk->freq = *prate / div;
+
+       return mclk->freq;
+}
+
+static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw,
+                                               unsigned long parent_rate)
+{
+       struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
+
+       return mclk->freq;
+}
+
+static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long parent_rate)
+{
+       struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
+       struct stm32_sai_sub_data *sai = mclk->sai_data;
+       unsigned int div;
+       int ret;
+
+       div = stm32_sai_get_clk_div(sai, parent_rate, rate);
+       if (div < 0)
+               return div;
+
+       ret = stm32_sai_set_clk_div(sai, div);
+       if (ret)
+               return ret;
+
+       mclk->freq = rate;
+
+       return 0;
+}
+
+static int stm32_sai_mclk_enable(struct clk_hw *hw)
+{
+       struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
+       struct stm32_sai_sub_data *sai = mclk->sai_data;
+
+       dev_dbg(&sai->pdev->dev, "Enable master clock\n");
+
+       return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+                                 SAI_XCR1_MCKEN, SAI_XCR1_MCKEN);
+}
+
+static void stm32_sai_mclk_disable(struct clk_hw *hw)
+{
+       struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
+       struct stm32_sai_sub_data *sai = mclk->sai_data;
+
+       dev_dbg(&sai->pdev->dev, "Disable master clock\n");
+
+       regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0);
+}
+
+static const struct clk_ops mclk_ops = {
+       .enable = stm32_sai_mclk_enable,
+       .disable = stm32_sai_mclk_disable,
+       .recalc_rate = stm32_sai_mclk_recalc_rate,
+       .round_rate = stm32_sai_mclk_round_rate,
+       .set_rate = stm32_sai_mclk_set_rate,
+};
+
+static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai)
+{
+       struct clk_hw *hw;
+       struct stm32_sai_mclk_data *mclk;
+       struct device *dev = &sai->pdev->dev;
+       const char *pname = __clk_get_name(sai->sai_ck);
+       char *mclk_name, *p, *s = (char *)pname;
+       int ret, i = 0;
+
+       mclk = devm_kzalloc(dev, sizeof(*mclk), GFP_KERNEL);
+       if (!mclk)
+               return -ENOMEM;
+
+       mclk_name = devm_kcalloc(dev, sizeof(char),
+                                SAI_MCLK_NAME_LEN, GFP_KERNEL);
+       if (!mclk_name)
+               return -ENOMEM;
+
+       /*
+        * Forge mclk clock name from parent clock name and suffix.
+        * String after "_" char is stripped in parent name.
+        */
+       p = mclk_name;
+       while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 7))) {
+               *p++ = *s++;
+               i++;
+       }
+       STM_SAI_IS_SUB_A(sai) ? strcat(p, "a_mclk") : strcat(p, "b_mclk");
+
+       mclk->hw.init = CLK_HW_INIT(mclk_name, pname, &mclk_ops, 0);
+       mclk->sai_data = sai;
+       hw = &mclk->hw;
+
+       dev_dbg(dev, "Register master clock %s\n", mclk_name);
+       ret = devm_clk_hw_register(&sai->pdev->dev, hw);
+       if (ret) {
+               dev_err(dev, "mclk register returned %d\n", ret);
+               return ret;
+       }
+       sai->sai_mclk = hw->clk;
+
+       /* register mclk provider */
+       return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
+}
+
 static irqreturn_t stm32_sai_isr(int irq, void *devid)
 {
        struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
@@ -312,15 +487,25 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
        struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
        int ret;
 
-       if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
+       if (dir == SND_SOC_CLOCK_OUT) {
                ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
                                         SAI_XCR1_NODIV,
                                         (unsigned int)~SAI_XCR1_NODIV);
                if (ret < 0)
                        return ret;
 
-               sai->mclk_rate = freq;
                dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
+               sai->mclk_rate = freq;
+
+               if (sai->sai_mclk) {
+                       ret = clk_set_rate_exclusive(sai->sai_mclk,
+                                                    sai->mclk_rate);
+                       if (ret) {
+                               dev_err(cpu_dai->dev,
+                                       "Could not set mclk rate\n");
+                               return ret;
+                       }
+               }
        }
 
        return 0;
@@ -715,15 +900,9 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
 {
        struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
        int cr1, mask, div = 0;
-       int sai_clk_rate, mclk_ratio, den, ret;
-       int version = sai->pdata->conf->version;
+       int sai_clk_rate, mclk_ratio, den;
        unsigned int rate = params_rate(params);
 
-       if (!sai->mclk_rate) {
-               dev_err(cpu_dai->dev, "Mclk rate is null\n");
-               return -EINVAL;
-       }
-
        if (!(rate % 11025))
                clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
        else
@@ -731,14 +910,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
        sai_clk_rate = clk_get_rate(sai->sai_ck);
 
        if (STM_SAI_IS_F4(sai->pdata)) {
-               /*
-                * mclk_rate = 256 * fs
-                * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
-                * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+               /* mclk on (NODIV=0)
+                *   mclk_rate = 256 * fs
+                *   MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
+                *   MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+                * mclk off (NODIV=1)
+                *   MCKDIV ignored. sck = sai_ck
                 */
-               if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
-                       div = DIV_ROUND_CLOSEST(sai_clk_rate,
-                                               2 * sai->mclk_rate);
+               if (!sai->mclk_rate)
+                       return 0;
+
+               if (2 * sai_clk_rate >= 3 * sai->mclk_rate) {
+                       div = stm32_sai_get_clk_div(sai, sai_clk_rate,
+                                                   2 * sai->mclk_rate);
+                       if (div < 0)
+                               return div;
+               }
        } else {
                /*
                 * TDM mode :
@@ -750,8 +937,10 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
                 * Note: NOMCK/NODIV correspond to same bit.
                 */
                if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) {
-                       div = DIV_ROUND_CLOSEST(sai_clk_rate,
-                                               (params_rate(params) * 128));
+                       div = stm32_sai_get_clk_div(sai, sai_clk_rate,
+                                                   rate * 128);
+                       if (div < 0)
+                               return div;
                } else {
                        if (sai->mclk_rate) {
                                mclk_ratio = sai->mclk_rate / rate;
@@ -764,31 +953,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
                                                mclk_ratio);
                                        return -EINVAL;
                                }
-                               div = DIV_ROUND_CLOSEST(sai_clk_rate,
-                                                       sai->mclk_rate);
+                               div = stm32_sai_get_clk_div(sai, sai_clk_rate,
+                                                           sai->mclk_rate);
+                               if (div < 0)
+                                       return div;
                        } else {
                                /* mclk-fs not set, master clock not active */
                                den = sai->fs_length * params_rate(params);
-                               div = DIV_ROUND_CLOSEST(sai_clk_rate, den);
+                               div = stm32_sai_get_clk_div(sai, sai_clk_rate,
+                                                           den);
+                               if (div < 0)
+                                       return div;
                        }
                }
        }
 
-       if (div > SAI_XCR1_MCKDIV_MAX(version)) {
-               dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
-               return -EINVAL;
-       }
-       dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
-
-       mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
-       cr1 = SAI_XCR1_MCKDIV_SET(div);
-       ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
-       if (ret < 0) {
-               dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
-               return ret;
-       }
-
-       return 0;
+       return stm32_sai_set_clk_div(sai, div);
 }
 
 static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
@@ -881,6 +1061,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
                           SAI_XCR1_NODIV);
 
        clk_disable_unprepare(sai->sai_ck);
+
+       clk_rate_exclusive_put(sai->sai_mclk);
+
        sai->substream = NULL;
 }
 
@@ -903,6 +1086,8 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
        struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
        int cr1 = 0, cr1_mask;
 
+       sai->cpu_dai = cpu_dai;
+
        sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
        /*
         * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice,
@@ -1181,6 +1366,23 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
                return PTR_ERR(sai->sai_ck);
        }
 
+       if (STM_SAI_IS_F4(sai->pdata))
+               return 0;
+
+       /* Register mclk provider if requested */
+       if (of_find_property(np, "#clock-cells", NULL)) {
+               ret = stm32_sai_add_mclk_provider(sai);
+               if (ret < 0)
+                       return ret;
+       } else {
+               sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK");
+               if (IS_ERR(sai->sai_mclk)) {
+                       if (PTR_ERR(sai->sai_mclk) != -ENOENT)
+                               return PTR_ERR(sai->sai_mclk);
+                       sai->sai_mclk = NULL;
+               }
+       }
+
        return 0;
 }