mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 16:24:13 +08:00
ASoC: stm32: sai: add h7 support
Add support of SAI on STM32H7 family. Signed-off-by: olivier moysan <olivier.moysan@st.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
3861da5801
commit
03e78a242a
@ -27,8 +27,17 @@
|
|||||||
|
|
||||||
#include "stm32_sai.h"
|
#include "stm32_sai.h"
|
||||||
|
|
||||||
|
static const struct stm32_sai_conf stm32_sai_conf_f4 = {
|
||||||
|
.version = SAI_STM32F4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stm32_sai_conf stm32_sai_conf_h7 = {
|
||||||
|
.version = SAI_STM32H7,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id stm32_sai_ids[] = {
|
static const struct of_device_id stm32_sai_ids[] = {
|
||||||
{ .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
|
{ .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 },
|
||||||
|
{ .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,7 +61,7 @@ static int stm32_sai_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
of_id = of_match_device(stm32_sai_ids, &pdev->dev);
|
of_id = of_match_device(stm32_sai_ids, &pdev->dev);
|
||||||
if (of_id)
|
if (of_id)
|
||||||
sai->version = (enum stm32_sai_version)of_id->data;
|
sai->conf = (struct stm32_sai_conf *)of_id->data;
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@
|
|||||||
#define STM_SAI_CLRFR_REGX 0x18
|
#define STM_SAI_CLRFR_REGX 0x18
|
||||||
#define STM_SAI_DR_REGX 0x1C
|
#define STM_SAI_DR_REGX 0x1C
|
||||||
|
|
||||||
|
/* Sub-block A registers, relative to sub-block A address */
|
||||||
|
#define STM_SAI_PDMCR_REGX 0x40
|
||||||
|
#define STM_SAI_PDMLY_REGX 0x44
|
||||||
|
|
||||||
/******************** Bit definition for SAI_GCR register *******************/
|
/******************** Bit definition for SAI_GCR register *******************/
|
||||||
#define SAI_GCR_SYNCIN_SHIFT 0
|
#define SAI_GCR_SYNCIN_SHIFT 0
|
||||||
#define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
|
#define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
|
||||||
@ -75,10 +79,11 @@
|
|||||||
#define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT)
|
#define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT)
|
||||||
|
|
||||||
#define SAI_XCR1_MCKDIV_SHIFT 20
|
#define SAI_XCR1_MCKDIV_SHIFT 20
|
||||||
#define SAI_XCR1_MCKDIV_WIDTH 4
|
#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6)
|
||||||
#define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
|
#define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\
|
||||||
|
SAI_XCR1_MCKDIV_SHIFT)
|
||||||
#define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT)
|
#define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT)
|
||||||
#define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
|
#define SAI_XCR1_MCKDIV_MAX(x) ((1 << SAI_XCR1_MCKDIV_WIDTH(x)) - 1)
|
||||||
|
|
||||||
#define SAI_XCR1_OSR_SHIFT 26
|
#define SAI_XCR1_OSR_SHIFT 26
|
||||||
#define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT)
|
#define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT)
|
||||||
@ -178,8 +183,65 @@
|
|||||||
#define SAI_XCLRFR_SHIFT 0
|
#define SAI_XCLRFR_SHIFT 0
|
||||||
#define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT)
|
#define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT)
|
||||||
|
|
||||||
|
/****************** Bit definition for SAI_PDMCR register ******************/
|
||||||
|
#define SAI_PDMCR_PDMEN BIT(0)
|
||||||
|
|
||||||
|
#define SAI_PDMCR_MICNBR_SHIFT 4
|
||||||
|
#define SAI_PDMCR_MICNBR_MASK GENMASK(5, SAI_PDMCR_MICNBR_SHIFT)
|
||||||
|
#define SAI_PDMCR_MICNBR_SET(x) ((x) << SAI_PDMCR_MICNBR_SHIFT)
|
||||||
|
|
||||||
|
#define SAI_PDMCR_CKEN1 BIT(8)
|
||||||
|
#define SAI_PDMCR_CKEN2 BIT(9)
|
||||||
|
#define SAI_PDMCR_CKEN3 BIT(10)
|
||||||
|
#define SAI_PDMCR_CKEN4 BIT(11)
|
||||||
|
|
||||||
|
/****************** Bit definition for (SAI_PDMDLY register ****************/
|
||||||
|
#define SAI_PDMDLY_1L_SHIFT 0
|
||||||
|
#define SAI_PDMDLY_1L_MASK GENMASK(2, SAI_PDMDLY_1L_SHIFT)
|
||||||
|
#define SAI_PDMDLY_1L_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_1R_SHIFT 4
|
||||||
|
#define SAI_PDMDLY_1R_MASK GENMASK(6, SAI_PDMDLY_1R_SHIFT)
|
||||||
|
#define SAI_PDMDLY_1R_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_2L_SHIFT 8
|
||||||
|
#define SAI_PDMDLY_2L_MASK GENMASK(10, SAI_PDMDLY_2L_SHIFT)
|
||||||
|
#define SAI_PDMDLY_2L_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_2R_SHIFT 12
|
||||||
|
#define SAI_PDMDLY_2R_MASK GENMASK(14, SAI_PDMDLY_2R_SHIFT)
|
||||||
|
#define SAI_PDMDLY_2R_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_3L_SHIFT 16
|
||||||
|
#define SAI_PDMDLY_3L_MASK GENMASK(18, SAI_PDMDLY_3L_SHIFT)
|
||||||
|
#define SAI_PDMDLY_3L_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_3R_SHIFT 20
|
||||||
|
#define SAI_PDMDLY_3R_MASK GENMASK(22, SAI_PDMDLY_3R_SHIFT)
|
||||||
|
#define SAI_PDMDLY_3R_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_4L_SHIFT 24
|
||||||
|
#define SAI_PDMDLY_4L_MASK GENMASK(26, SAI_PDMDLY_4L_SHIFT)
|
||||||
|
#define SAI_PDMDLY_4L_WIDTH 3
|
||||||
|
|
||||||
|
#define SAI_PDMDLY_4R_SHIFT 28
|
||||||
|
#define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT)
|
||||||
|
#define SAI_PDMDLY_4R_WIDTH 3
|
||||||
|
|
||||||
|
#define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4)
|
||||||
|
#define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7)
|
||||||
|
|
||||||
enum stm32_sai_version {
|
enum stm32_sai_version {
|
||||||
SAI_STM32F4
|
SAI_STM32F4,
|
||||||
|
SAI_STM32H7
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct stm32_sai_conf - SAI configuration
|
||||||
|
* @version: SAI version
|
||||||
|
*/
|
||||||
|
struct stm32_sai_conf {
|
||||||
|
int version;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -194,6 +256,6 @@ struct stm32_sai_data {
|
|||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct clk *clk_x8k;
|
struct clk *clk_x8k;
|
||||||
struct clk *clk_x11k;
|
struct clk *clk_x11k;
|
||||||
int version;
|
struct stm32_sai_conf *conf;
|
||||||
int irq;
|
int irq;
|
||||||
};
|
};
|
||||||
|
@ -51,12 +51,15 @@
|
|||||||
#define STM_SAI_A_ID 0x0
|
#define STM_SAI_A_ID 0x0
|
||||||
#define STM_SAI_B_ID 0x1
|
#define STM_SAI_B_ID 0x1
|
||||||
|
|
||||||
|
#define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID)
|
||||||
|
#define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID)
|
||||||
#define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B")
|
#define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
|
* struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
|
||||||
* @pdev: device data pointer
|
* @pdev: device data pointer
|
||||||
* @regmap: SAI register map pointer
|
* @regmap: SAI register map pointer
|
||||||
|
* @regmap_config: SAI sub block register map configuration pointer
|
||||||
* @dma_params: dma configuration data for rx or tx channel
|
* @dma_params: dma configuration data for rx or tx channel
|
||||||
* @cpu_dai_drv: DAI driver data pointer
|
* @cpu_dai_drv: DAI driver data pointer
|
||||||
* @cpu_dai: DAI runtime data pointer
|
* @cpu_dai: DAI runtime data pointer
|
||||||
@ -79,6 +82,7 @@
|
|||||||
struct stm32_sai_sub_data {
|
struct stm32_sai_sub_data {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
const struct regmap_config *regmap_config;
|
||||||
struct snd_dmaengine_dai_dma_data dma_params;
|
struct snd_dmaengine_dai_dma_data dma_params;
|
||||||
struct snd_soc_dai_driver *cpu_dai_drv;
|
struct snd_soc_dai_driver *cpu_dai_drv;
|
||||||
struct snd_soc_dai *cpu_dai;
|
struct snd_soc_dai *cpu_dai;
|
||||||
@ -118,6 +122,8 @@ static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
|
|||||||
case STM_SAI_SR_REGX:
|
case STM_SAI_SR_REGX:
|
||||||
case STM_SAI_CLRFR_REGX:
|
case STM_SAI_CLRFR_REGX:
|
||||||
case STM_SAI_DR_REGX:
|
case STM_SAI_DR_REGX:
|
||||||
|
case STM_SAI_PDMCR_REGX:
|
||||||
|
case STM_SAI_PDMLY_REGX:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@ -145,13 +151,15 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
|
|||||||
case STM_SAI_SR_REGX:
|
case STM_SAI_SR_REGX:
|
||||||
case STM_SAI_CLRFR_REGX:
|
case STM_SAI_CLRFR_REGX:
|
||||||
case STM_SAI_DR_REGX:
|
case STM_SAI_DR_REGX:
|
||||||
|
case STM_SAI_PDMCR_REGX:
|
||||||
|
case STM_SAI_PDMLY_REGX:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct regmap_config stm32_sai_sub_regmap_config = {
|
static const struct regmap_config stm32_sai_sub_regmap_config_f4 = {
|
||||||
.reg_bits = 32,
|
.reg_bits = 32,
|
||||||
.reg_stride = 4,
|
.reg_stride = 4,
|
||||||
.val_bits = 32,
|
.val_bits = 32,
|
||||||
@ -162,6 +170,17 @@ static const struct regmap_config stm32_sai_sub_regmap_config = {
|
|||||||
.fast_io = true,
|
.fast_io = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config stm32_sai_sub_regmap_config_h7 = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.val_bits = 32,
|
||||||
|
.max_register = STM_SAI_PDMLY_REGX,
|
||||||
|
.readable_reg = stm32_sai_sub_readable_reg,
|
||||||
|
.volatile_reg = stm32_sai_sub_volatile_reg,
|
||||||
|
.writeable_reg = stm32_sai_sub_writeable_reg,
|
||||||
|
.fast_io = true,
|
||||||
|
};
|
||||||
|
|
||||||
static irqreturn_t stm32_sai_isr(int irq, void *devid)
|
static irqreturn_t stm32_sai_isr(int irq, void *devid)
|
||||||
{
|
{
|
||||||
struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
|
struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
|
||||||
@ -551,7 +570,8 @@ 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);
|
struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
|
||||||
int cr1, mask, div = 0;
|
int cr1, mask, div = 0;
|
||||||
int sai_clk_rate, ret;
|
int sai_clk_rate, mclk_ratio, den, ret;
|
||||||
|
int version = sai->pdata->conf->version;
|
||||||
|
|
||||||
if (!sai->mclk_rate) {
|
if (!sai->mclk_rate) {
|
||||||
dev_err(cpu_dai->dev, "Mclk rate is null\n");
|
dev_err(cpu_dai->dev, "Mclk rate is null\n");
|
||||||
@ -564,21 +584,53 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
|
|||||||
clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
|
clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
|
||||||
sai_clk_rate = clk_get_rate(sai->sai_ck);
|
sai_clk_rate = clk_get_rate(sai->sai_ck);
|
||||||
|
|
||||||
|
if (STM_SAI_IS_F4(sai->pdata)) {
|
||||||
/*
|
/*
|
||||||
* mclk_rate = 256 * fs
|
* mclk_rate = 256 * fs
|
||||||
* MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
|
* MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
|
||||||
* MCKDIV = sai_ck / (2 * mclk_rate) otherwise
|
* MCKDIV = sai_ck / (2 * mclk_rate) otherwise
|
||||||
*/
|
*/
|
||||||
if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
|
if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
|
||||||
div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
|
div = DIV_ROUND_CLOSEST(sai_clk_rate,
|
||||||
|
2 * sai->mclk_rate);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* TDM mode :
|
||||||
|
* mclk on
|
||||||
|
* MCKDIV = sai_ck / (ws x 256) (NOMCK=0. OSR=0)
|
||||||
|
* MCKDIV = sai_ck / (ws x 512) (NOMCK=0. OSR=1)
|
||||||
|
* mclk off
|
||||||
|
* MCKDIV = sai_ck / (frl x ws) (NOMCK=1)
|
||||||
|
* Note: NOMCK/NODIV correspond to same bit.
|
||||||
|
*/
|
||||||
|
if (sai->mclk_rate) {
|
||||||
|
mclk_ratio = sai->mclk_rate / params_rate(params);
|
||||||
|
if (mclk_ratio != 256) {
|
||||||
|
if (mclk_ratio == 512) {
|
||||||
|
mask = SAI_XCR1_OSR;
|
||||||
|
cr1 = SAI_XCR1_OSR;
|
||||||
|
} else {
|
||||||
|
dev_err(cpu_dai->dev,
|
||||||
|
"Wrong mclk ratio %d\n",
|
||||||
|
mclk_ratio);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate);
|
||||||
|
} else {
|
||||||
|
/* mclk-fs not set, master clock not active. NOMCK=1 */
|
||||||
|
den = sai->fs_length * params_rate(params);
|
||||||
|
div = DIV_ROUND_CLOSEST(sai_clk_rate, den);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (div > SAI_XCR1_MCKDIV_MAX) {
|
if (div > SAI_XCR1_MCKDIV_MAX(version)) {
|
||||||
dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
|
dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
|
dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
|
||||||
|
|
||||||
mask = SAI_XCR1_MCKDIV_MASK;
|
mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version));
|
||||||
cr1 = SAI_XCR1_MCKDIV_SET(div);
|
cr1 = SAI_XCR1_MCKDIV_SET(div);
|
||||||
ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
|
ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -780,8 +832,18 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,
|
|||||||
return PTR_ERR(base);
|
return PTR_ERR(base);
|
||||||
|
|
||||||
sai->phys_addr = res->start;
|
sai->phys_addr = res->start;
|
||||||
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", base,
|
|
||||||
&stm32_sai_sub_regmap_config);
|
sai->regmap_config = &stm32_sai_sub_regmap_config_f4;
|
||||||
|
/* Note: PDM registers not available for H7 sub-block B */
|
||||||
|
if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai))
|
||||||
|
sai->regmap_config = &stm32_sai_sub_regmap_config_h7;
|
||||||
|
|
||||||
|
sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck",
|
||||||
|
base, sai->regmap_config);
|
||||||
|
if (IS_ERR(sai->regmap)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to initialize MMIO\n");
|
||||||
|
return PTR_ERR(sai->regmap);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get direction property */
|
/* Get direction property */
|
||||||
if (of_property_match_string(np, "dma-names", "tx") >= 0) {
|
if (of_property_match_string(np, "dma-names", "tx") >= 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user