-/**
- ******************************************************************************
- * @file sf_pdm.c
- * @author StarFive Technology
- * @version V1.0
- * @date 05/27/2021
- * @brief
- ******************************************************************************
- * @copy
- *
- * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
- * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
- * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
- * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
- * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
- * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
- *
- * <h2><center>© COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
- */
-
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PDM driver for the StarFive JH7110 SoC
+ *
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/reset.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <sound/tlv.h>
#include "starfive_pdm.h"
-#define AUDIOC_CLK (12288000)
-#define PDM_MUL (128)
+#define AUDIOC_CLK 12288000
+#define PDM_MUL 128
struct sf_pdm {
struct regmap *pdm_map;
- struct regmap *clk_map;
- struct clk *clk;
+ struct clk *clk_pdm_apb;
+ struct clk *clk_pdm_dmic;
+ struct clk *clk_dmic0_bclk;
+ struct clk *clk_dmic0_lrck;
+ struct clk *clk_dmic1_bclk;
+ struct clk *clk_dmic1_lrck;
+ struct clk *clk_apb0;
+ struct clk *clk_i2srx_3ch_bclk;
+ struct reset_control *rst_pdm_dmic;
+ struct reset_control *rst_pdm_apb;
};
static const DECLARE_TLV_DB_SCALE(volume_tlv, -9450, 150, 0);
SOC_SINGLE("DC offset", PDM_DC_SCALE0, 8, 0xFFFFF, 0),
};
-static int sf_pdm_set_mclk(struct regmap *map,
- unsigned int clk, unsigned int weight)
+static int sf_pdm_set_mclk(struct sf_pdm *priv, unsigned int clk,
+ unsigned int width)
{
- int mclk_div,bclk_div,lrclk_div;
- u32 pdm_div;
-
/*
audio source clk:12288000, mclk_div:4, mclk:3M
- support 8K/16K/32K/48K sample reate
- suapport 16/24/32 bit weight
- bit weight 32
+ support 8K/16K/32K/48K sample rate
+ support 16/24/32 bit width
+ bit width 32
mclk bclk lrclk
3M 1.5M 48K
3M 1M 32K
3M 0.5M 16K
3M 0.25M 8K
- bit weight 24,set lrclk_div as 32
+ bit width 24, set lrclk_div as 32
mclk bclk lrclk
3M 1.5M 48K
3M 1M 32K
3M 0.5M 16K
3M 0.25M 8K
- bit weight 16
+ bit width 16
mclk bclk lrclk
3M 0.75M 48K
3M 0.5M 32K
case 48000:
break;
default:
- printk(KERN_ERR "sample rate:%d\n", clk);
+ pr_err("PDM: not support sample rate:%d\n", clk);
return -EINVAL;
}
- switch (weight) {
+ switch (width) {
case 16:
case 24:
case 32:
break;
default:
- printk(KERN_ERR "bit weight:%d\n", weight);
+ pr_err("PDM: not support bit width %d\n", width);
return -EINVAL;
}
- if (24 == weight) {
- weight = 32;
- }
-
- mclk_div = 4;
- bclk_div = AUDIOC_CLK/mclk_div/(clk*weight);
- lrclk_div = weight;
-
- /* PDM MCLK = 128*LRCLK */
- pdm_div = AUDIOC_CLK/(PDM_MUL*clk);
+ if (width == 24)
+ width = 32;
- regmap_update_bits(map, AUDIO_CLK_ADC_MCLK, 0x0F, mclk_div);
- regmap_update_bits(map, AUDIO_CLK_I2SADC_BCLK, 0x1F, bclk_div);
- regmap_update_bits(map, AUDIO_CLK_ADC_LRCLK, 0x3F, lrclk_div);
- regmap_update_bits(map, AUDIO_CLK_PDM_CLK, 0x0F, pdm_div);
+ /* PDM MCLK = 128 * LRCLK */
+ clk_set_rate(priv->clk_dmic0_bclk, clk*width);
+ clk_set_rate(priv->clk_dmic0_lrck, clk);
+ clk_set_rate(priv->clk_pdm_dmic, PDM_MUL * clk); //MCLK
return 0;
}
static void sf_pdm_enable(struct regmap *map)
{
/* Enable PDM */
- regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01<<PDM_DMIC_RVOL_OFFSET, 0);
- regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01<<PDM_DMIC_LVOL_OFFSET, 0);
+ regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01 << PDM_DMIC_RVOL_OFFSET, 0);
+ regmap_update_bits(map, PDM_DMIC_CTRL0, 0x01 << PDM_DMIC_LVOL_OFFSET, 0);
}
static void sf_pdm_disable(struct regmap *map)
{
regmap_update_bits(map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_RVOL_OFFSET, 0x01<<PDM_DMIC_RVOL_OFFSET);
+ 0x01 << PDM_DMIC_RVOL_OFFSET, 0x01 << PDM_DMIC_RVOL_OFFSET);
regmap_update_bits(map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_LVOL_OFFSET, 0x01<<PDM_DMIC_LVOL_OFFSET);
+ 0x01 << PDM_DMIC_LVOL_OFFSET, 0x01 << PDM_DMIC_LVOL_OFFSET);
}
static int sf_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
return -EINVAL;
}
- ret = sf_pdm_set_mclk(priv->clk_map, rate, width);
+ ret = sf_pdm_set_mclk(priv, rate, width);
if (ret < 0) {
dev_err(dai->dev, "unsupported sample rate\n");
return -EINVAL;
/* Reset */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_SW_RSTN_OFFSET, 0x00);
+ 0x01 << PDM_DMIC_SW_RSTN_OFFSET, 0x00);
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_SW_RSTN_OFFSET, 0x01<<PDM_DMIC_SW_RSTN_OFFSET);
+ 0x01 << PDM_DMIC_SW_RSTN_OFFSET, 0x01 << PDM_DMIC_SW_RSTN_OFFSET);
/* Make sure the device is initially disabled */
sf_pdm_disable(priv->pdm_map);
/* MUTE */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x3F<<PDM_DMIC_VOL_OFFSET, 0x3F<<PDM_DMIC_VOL_OFFSET);
+ 0x3F << PDM_DMIC_VOL_OFFSET, 0x3F << PDM_DMIC_VOL_OFFSET);
/* UNMUTE */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x3F<<PDM_DMIC_VOL_OFFSET, 0);
+ 0x3F << PDM_DMIC_VOL_OFFSET, 0);
/* enable high pass filter */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_ENHPF_OFFSET, 0x01<<PDM_DMIC_ENHPF_OFFSET);
+ 0x01 << PDM_DMIC_ENHPF_OFFSET, 0x01 << PDM_DMIC_ENHPF_OFFSET);
/* i2s slaver mode */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_I2SMODE_OFFSET, 0x01<<PDM_DMIC_I2SMODE_OFFSET);
+ 0x01 << PDM_DMIC_I2SMODE_OFFSET, 0x01 << PDM_DMIC_I2SMODE_OFFSET);
/* disable fast mode */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_FASTMODE_OFFSET, 0);
+ 0x01 << PDM_DMIC_FASTMODE_OFFSET, 0);
/* enable dc bypass mode */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x01<<PDM_DMIC_DCBPS_OFFSET, 0);
+ 0x01 << PDM_DMIC_DCBPS_OFFSET, 0);
/* dmic msb shift 0 */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x07<<PDM_DMIC_MSB_SHIFT_OFFSET, 0);
+ 0x07 << PDM_DMIC_MSB_SHIFT_OFFSET, 0);
/* scale:0 */
regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, 0x3F, 0x08);
/* DC offset:0 */
regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0,
- 0xFFFFF<<PDM_DMIC_DCOFF1_OFFSET, 0xC0005<<PDM_DMIC_DCOFF1_OFFSET);
+ 0xFFFFF << PDM_DMIC_DCOFF1_OFFSET, 0xC0005 << PDM_DMIC_DCOFF1_OFFSET);
return 0;
}
/* MUTE */
regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0,
- 0x3F<<PDM_DMIC_VOL_OFFSET, 0x3F<<PDM_DMIC_VOL_OFFSET);
+ 0x3F << PDM_DMIC_VOL_OFFSET, 0x3F << PDM_DMIC_VOL_OFFSET);
return 0;
}
-#define SF_PCM_RATE (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
- SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_48000)
+#define SF_PCM_RATE (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_48000)
static struct snd_soc_dai_driver sf_pdm_dai_drv = {
.name = "PDM",
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
- .rates = SF_PCM_RATE,
- .formats = SNDRV_PCM_FMTBIT_S16_LE|\
- SNDRV_PCM_FMTBIT_S24_LE|\
- SNDRV_PCM_FMTBIT_S32_LE,
+ .rates = SF_PCM_RATE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE|\
+ SNDRV_PCM_FMTBIT_S24_LE|\
+ SNDRV_PCM_FMTBIT_S32_LE,
},
.ops = &sf_pdm_dai_ops,
.probe = sf_pdm_dai_probe,
.max_register = 0x100,
};
+static int sf_pdm_clock_init(struct platform_device *pdev, struct sf_pdm *priv)
+{
+ int ret;
+ unsigned int val = 0;
+
+ priv->rst_pdm_dmic = devm_reset_control_get_exclusive(&pdev->dev, "pdm_dmic");
+ if (IS_ERR(priv->rst_pdm_dmic)) {
+ dev_err(&pdev->dev, "failed to get pdm_dmic reset control\n");
+ return PTR_ERR(priv->rst_pdm_dmic);
+ }
+
+ priv->rst_pdm_apb = devm_reset_control_get_exclusive(&pdev->dev, "pdm_apb");
+ if (IS_ERR(priv->rst_pdm_apb)) {
+ dev_err(&pdev->dev, "failed to get pdm_apb reset control\n");
+ return PTR_ERR(priv->rst_pdm_apb);
+ }
+
+ priv->clk_apb0 = devm_clk_get(&pdev->dev, "clk_apb0");
+ if (IS_ERR(priv->clk_apb0)) {
+ dev_err(&pdev->dev, "failed to get clk_apb0\n");
+ return PTR_ERR(priv->clk_apb0);
+ }
+
+ priv->clk_pdm_apb = devm_clk_get(&pdev->dev, "pdm_apb");
+ if (IS_ERR(priv->clk_pdm_apb)) {
+ dev_err(&pdev->dev, "failed to get clk_pdm_apb\n");
+ return PTR_ERR(priv->clk_pdm_apb);
+ }
+
+ priv->clk_pdm_dmic = devm_clk_get(&pdev->dev, "pdm_dmic");
+ if (IS_ERR(priv->clk_pdm_dmic)) {
+ dev_err(&pdev->dev, "failed to get clk_pdm_dmic\n");
+ return PTR_ERR(priv->clk_pdm_dmic);
+ }
+
+ priv->clk_dmic0_bclk = devm_clk_get(&pdev->dev, "pdm_dmic0_bclk");
+ if (IS_ERR(priv->clk_dmic0_bclk)) {
+ dev_err(&pdev->dev, "failed to get clk_dmic0_bclk\n");
+ return PTR_ERR(priv->clk_dmic0_bclk);
+ }
+
+ priv->clk_dmic0_lrck = devm_clk_get(&pdev->dev, "pdm_dmic0_lrck");
+ if (IS_ERR(priv->clk_dmic0_lrck)) {
+ dev_err(&pdev->dev, "failed to get clk_dmic0_bclk\n");
+ return PTR_ERR(priv->clk_dmic0_lrck);
+ }
+
+ priv->clk_dmic1_bclk = devm_clk_get(&pdev->dev, "pdm_dmic1_bclk");
+ if (IS_ERR(priv->clk_dmic1_bclk)) {
+ dev_err(&pdev->dev, "failed to get clk_dmic1_bclk\n");
+ return PTR_ERR(priv->clk_dmic1_bclk);
+ }
+
+ priv->clk_dmic1_lrck = devm_clk_get(&pdev->dev, "pdm_dmic1_lrck");
+ if (IS_ERR(priv->clk_dmic1_lrck)) {
+ dev_err(&pdev->dev, "failed to get clk_dmic1_bclk\n");
+ return PTR_ERR(priv->clk_dmic1_lrck);
+ }
+
+ priv->clk_i2srx_3ch_bclk = devm_clk_get(&pdev->dev, "u0_i2srx_3ch_bclk");
+ if (IS_ERR(priv->clk_i2srx_3ch_bclk)) {
+ dev_err(&pdev->dev, "failed to get clk_i2srx_3ch_bclk\n");
+ return PTR_ERR(priv->clk_i2srx_3ch_bclk);
+ }
+
+ ret = clk_prepare_enable(priv->clk_pdm_dmic);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_pdm_dmic\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_apb0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_apb0\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_pdm_apb);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_pdm_apb\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_i2srx_3ch_bclk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_i2srx_3ch_bclk\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_dmic0_bclk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_dmic0_bclk\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_dmic0_lrck);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_dmic0_lrck\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_dmic1_bclk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_dmic1_bclk\n");
+ goto err_clk_disable;
+ }
+
+ ret = clk_prepare_enable(priv->clk_dmic1_lrck);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare enable clk_dmic1_lrck\n");
+ goto err_clk_disable;
+ }
+
+ ret = reset_control_deassert(dev->rst_pdm_dmic);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to deassert pdm_dmic\n");
+ goto err_clk_disable;
+ }
+
+ ret = reset_control_deassert(dev->rst_pdm_apb);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to deassert pdm_apb\n");
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ return ret;
+}
+
static int sf_pdm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sf_pdm *priv;
struct resource *res;
void __iomem *regs;
+ int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return PTR_ERR(priv->pdm_map);
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-clk");
- regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- priv->clk_map = devm_regmap_init_mmio(dev, regs, &sf_audio_clk_regmap_cfg);
- if (IS_ERR(priv->clk_map)) {
- dev_err(dev, "failed to init regmap: %ld\n",
- PTR_ERR(priv->clk_map));
- return PTR_ERR(priv->clk_map);
+ ret = sf_pdm_clock_init(pdev, priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable audio-pdm clock\n");
+ return ret;
}
return devm_snd_soc_register_component(dev, &sf_pdm_component_drv,
};
module_platform_driver(sf_pdm_driver);
-MODULE_AUTHOR("michael.yan <michael.yan@starfivetech.com>");
-MODULE_DESCRIPTION("starfive PDM Controller Driver");
+MODULE_AUTHOR("Walker Chen <walker.chen@starfivetech.com>");
+MODULE_DESCRIPTION("Starfive PDM Controller Driver");
MODULE_LICENSE("GPL v2");
-/**
- ******************************************************************************
- * @file sf_pdm.h
- * @author StarFive Technology
- * @version V1.0
- * @date 05/27/2021
- * @brief
- ******************************************************************************
- * @copy
- *
- * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
- * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
- * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY
- * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
- * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
- * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
- *
- * <h2><center>© COPYRIGHT 20120 Shanghai StarFive Technology Co., Ltd. </center></h2>
- */
-
-#ifndef __SND_SOC_STARFIVE_SPDIF_H
-#define __SND_SOC_STARFIVE_PDM_H
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * PDM driver for the StarFive JH7110 SoC
+ *
+ * Copyright (C) 2021 StarFive Technology Co., Ltd.
+ */
+#ifndef __SND_SOC_STARFIVE_PDM_H__
+#define __SND_SOC_STARFIVE_PDM_H__
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/types.h>
-#define PDM_DMIC_CTRL0 (0x00)
-#define PDM_DC_SCALE0 (0x04)
-#define PDM_DMIC_CTRL1 (0x10)
-#define PDM_DC_SCALE1 (0x14)
+#define PDM_DMIC_CTRL0 0x00
+#define PDM_DC_SCALE0 0x04
+#define PDM_DMIC_CTRL1 0x10
+#define PDM_DC_SCALE1 0x14
/* PDM CTRL OFFSET */
-#define PDM_DMIC_MSB_SHIFT_OFFSET (1)
-#define PDM_DMIC_VOL_OFFSET (16)
-#define PDM_DMIC_RVOL_OFFSET (22)
-#define PDM_DMIC_LVOL_OFFSET (23)
-#define PDM_DMIC_I2SMODE_OFFSET (24)
-#define PDM_DMIC_ENHPF_OFFSET (28)
-#define PDM_DMIC_FASTMODE_OFFSET (29)
-#define PDM_DMIC_DCBPS_OFFSET (30)
-#define PDM_DMIC_SW_RSTN_OFFSET (31)
+#define PDM_DMIC_MSB_SHIFT_OFFSET 1
+#define PDM_DMIC_VOL_OFFSET 16
+#define PDM_DMIC_RVOL_OFFSET 22
+#define PDM_DMIC_LVOL_OFFSET 23
+#define PDM_DMIC_I2SMODE_OFFSET 24
+#define PDM_DMIC_ENHPF_OFFSET 28
+#define PDM_DMIC_FASTMODE_OFFSET 29
+#define PDM_DMIC_DCBPS_OFFSET 30
+#define PDM_DMIC_SW_RSTN_OFFSET 31
/* PDM SCALE OFFSET */
-#define PDM_DMIC_DCOFF3_OFFSET (24)
-#define PDM_DMIC_DCOFF2_OFFSET (16)
-#define PDM_DMIC_DCOFF1_OFFSET (8)
-#define PDM_DMIC_SCALE_OFFSET (0)
+#define PDM_DMIC_DCOFF3_OFFSET 24
+#define PDM_DMIC_DCOFF2_OFFSET 16
+#define PDM_DMIC_DCOFF1_OFFSET 8
+#define PDM_DMIC_SCALE_OFFSET 0
-#define AUDIO_CLK_ADC_MCLK 0x0
-#define AUDIO_CLK_I2SADC_BCLK 0xC
-#define AUDIO_CLK_ADC_LRCLK 0x14
-#define AUDIO_CLK_PDM_CLK 0x1C
+#define AUDIO_CLK_ADC_MCLK 0x0
+#define AUDIO_CLK_I2SADC_BCLK 0xC
+#define AUDIO_CLK_ADC_LRCLK 0x14
+#define AUDIO_CLK_PDM_CLK 0x1C
-#endif /* __SND_SOC_STARFIVE_PDM_H */
+#endif /* __SND_SOC_STARFIVE_PDM_H__ */