diff options
author | olivier moysan <olivier.moysan@st.com> | 2017-06-16 08:16:24 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-06-16 13:59:03 -0400 |
commit | 03e78a242a15eca68e5c7cb606c94959382e2b18 (patch) | |
tree | d76d53c35180a2bb7d2dada264bce63fa5152499 | |
parent | 3861da5801f59f3e9252b6a5db92cfa71629995c (diff) |
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>
-rw-r--r-- | sound/soc/stm/stm32_sai.c | 13 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.h | 72 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 92 |
3 files changed, 155 insertions, 22 deletions
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 6159d66c2c54..f7713314913b 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c | |||
@@ -27,8 +27,17 @@ | |||
27 | 27 | ||
28 | #include "stm32_sai.h" | 28 | #include "stm32_sai.h" |
29 | 29 | ||
30 | static const struct stm32_sai_conf stm32_sai_conf_f4 = { | ||
31 | .version = SAI_STM32F4, | ||
32 | }; | ||
33 | |||
34 | static const struct stm32_sai_conf stm32_sai_conf_h7 = { | ||
35 | .version = SAI_STM32H7, | ||
36 | }; | ||
37 | |||
30 | static const struct of_device_id stm32_sai_ids[] = { | 38 | static const struct of_device_id stm32_sai_ids[] = { |
31 | { .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 }, | 39 | { .compatible = "st,stm32f4-sai", .data = (void *)&stm32_sai_conf_f4 }, |
40 | { .compatible = "st,stm32h7-sai", .data = (void *)&stm32_sai_conf_h7 }, | ||
32 | {} | 41 | {} |
33 | }; | 42 | }; |
34 | 43 | ||
@@ -52,7 +61,7 @@ static int stm32_sai_probe(struct platform_device *pdev) | |||
52 | 61 | ||
53 | of_id = of_match_device(stm32_sai_ids, &pdev->dev); | 62 | of_id = of_match_device(stm32_sai_ids, &pdev->dev); |
54 | if (of_id) | 63 | if (of_id) |
55 | sai->version = (enum stm32_sai_version)of_id->data; | 64 | sai->conf = (struct stm32_sai_conf *)of_id->data; |
56 | else | 65 | else |
57 | return -EINVAL; | 66 | return -EINVAL; |
58 | 67 | ||
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index 270be93b845e..889974dc62d9 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h | |||
@@ -31,6 +31,10 @@ | |||
31 | #define STM_SAI_CLRFR_REGX 0x18 | 31 | #define STM_SAI_CLRFR_REGX 0x18 |
32 | #define STM_SAI_DR_REGX 0x1C | 32 | #define STM_SAI_DR_REGX 0x1C |
33 | 33 | ||
34 | /* Sub-block A registers, relative to sub-block A address */ | ||
35 | #define STM_SAI_PDMCR_REGX 0x40 | ||
36 | #define STM_SAI_PDMLY_REGX 0x44 | ||
37 | |||
34 | /******************** Bit definition for SAI_GCR register *******************/ | 38 | /******************** Bit definition for SAI_GCR register *******************/ |
35 | #define SAI_GCR_SYNCIN_SHIFT 0 | 39 | #define SAI_GCR_SYNCIN_SHIFT 0 |
36 | #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) | 40 | #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) |
@@ -75,10 +79,11 @@ | |||
75 | #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) | 79 | #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) |
76 | 80 | ||
77 | #define SAI_XCR1_MCKDIV_SHIFT 20 | 81 | #define SAI_XCR1_MCKDIV_SHIFT 20 |
78 | #define SAI_XCR1_MCKDIV_WIDTH 4 | 82 | #define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6) |
79 | #define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT) | 83 | #define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\ |
84 | SAI_XCR1_MCKDIV_SHIFT) | ||
80 | #define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT) | 85 | #define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT) |
81 | #define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1) | 86 | #define SAI_XCR1_MCKDIV_MAX(x) ((1 << SAI_XCR1_MCKDIV_WIDTH(x)) - 1) |
82 | 87 | ||
83 | #define SAI_XCR1_OSR_SHIFT 26 | 88 | #define SAI_XCR1_OSR_SHIFT 26 |
84 | #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) | 89 | #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) |
@@ -178,8 +183,65 @@ | |||
178 | #define SAI_XCLRFR_SHIFT 0 | 183 | #define SAI_XCLRFR_SHIFT 0 |
179 | #define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT) | 184 | #define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT) |
180 | 185 | ||
186 | /****************** Bit definition for SAI_PDMCR register ******************/ | ||
187 | #define SAI_PDMCR_PDMEN BIT(0) | ||
188 | |||
189 | #define SAI_PDMCR_MICNBR_SHIFT 4 | ||
190 | #define SAI_PDMCR_MICNBR_MASK GENMASK(5, SAI_PDMCR_MICNBR_SHIFT) | ||
191 | #define SAI_PDMCR_MICNBR_SET(x) ((x) << SAI_PDMCR_MICNBR_SHIFT) | ||
192 | |||
193 | #define SAI_PDMCR_CKEN1 BIT(8) | ||
194 | #define SAI_PDMCR_CKEN2 BIT(9) | ||
195 | #define SAI_PDMCR_CKEN3 BIT(10) | ||
196 | #define SAI_PDMCR_CKEN4 BIT(11) | ||
197 | |||
198 | /****************** Bit definition for (SAI_PDMDLY register ****************/ | ||
199 | #define SAI_PDMDLY_1L_SHIFT 0 | ||
200 | #define SAI_PDMDLY_1L_MASK GENMASK(2, SAI_PDMDLY_1L_SHIFT) | ||
201 | #define SAI_PDMDLY_1L_WIDTH 3 | ||
202 | |||
203 | #define SAI_PDMDLY_1R_SHIFT 4 | ||
204 | #define SAI_PDMDLY_1R_MASK GENMASK(6, SAI_PDMDLY_1R_SHIFT) | ||
205 | #define SAI_PDMDLY_1R_WIDTH 3 | ||
206 | |||
207 | #define SAI_PDMDLY_2L_SHIFT 8 | ||
208 | #define SAI_PDMDLY_2L_MASK GENMASK(10, SAI_PDMDLY_2L_SHIFT) | ||
209 | #define SAI_PDMDLY_2L_WIDTH 3 | ||
210 | |||
211 | #define SAI_PDMDLY_2R_SHIFT 12 | ||
212 | #define SAI_PDMDLY_2R_MASK GENMASK(14, SAI_PDMDLY_2R_SHIFT) | ||
213 | #define SAI_PDMDLY_2R_WIDTH 3 | ||
214 | |||
215 | #define SAI_PDMDLY_3L_SHIFT 16 | ||
216 | #define SAI_PDMDLY_3L_MASK GENMASK(18, SAI_PDMDLY_3L_SHIFT) | ||
217 | #define SAI_PDMDLY_3L_WIDTH 3 | ||
218 | |||
219 | #define SAI_PDMDLY_3R_SHIFT 20 | ||
220 | #define SAI_PDMDLY_3R_MASK GENMASK(22, SAI_PDMDLY_3R_SHIFT) | ||
221 | #define SAI_PDMDLY_3R_WIDTH 3 | ||
222 | |||
223 | #define SAI_PDMDLY_4L_SHIFT 24 | ||
224 | #define SAI_PDMDLY_4L_MASK GENMASK(26, SAI_PDMDLY_4L_SHIFT) | ||
225 | #define SAI_PDMDLY_4L_WIDTH 3 | ||
226 | |||
227 | #define SAI_PDMDLY_4R_SHIFT 28 | ||
228 | #define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT) | ||
229 | #define SAI_PDMDLY_4R_WIDTH 3 | ||
230 | |||
231 | #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) | ||
232 | #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) | ||
233 | |||
181 | enum stm32_sai_version { | 234 | enum stm32_sai_version { |
182 | SAI_STM32F4 | 235 | SAI_STM32F4, |
236 | SAI_STM32H7 | ||
237 | }; | ||
238 | |||
239 | /** | ||
240 | * struct stm32_sai_conf - SAI configuration | ||
241 | * @version: SAI version | ||
242 | */ | ||
243 | struct stm32_sai_conf { | ||
244 | int version; | ||
183 | }; | 245 | }; |
184 | 246 | ||
185 | /** | 247 | /** |
@@ -194,6 +256,6 @@ struct stm32_sai_data { | |||
194 | struct platform_device *pdev; | 256 | struct platform_device *pdev; |
195 | struct clk *clk_x8k; | 257 | struct clk *clk_x8k; |
196 | struct clk *clk_x11k; | 258 | struct clk *clk_x11k; |
197 | int version; | 259 | struct stm32_sai_conf *conf; |
198 | int irq; | 260 | int irq; |
199 | }; | 261 | }; |
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index ce48c02db051..ba3fdc777ed8 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c | |||
@@ -51,12 +51,15 @@ | |||
51 | #define STM_SAI_A_ID 0x0 | 51 | #define STM_SAI_A_ID 0x0 |
52 | #define STM_SAI_B_ID 0x1 | 52 | #define STM_SAI_B_ID 0x1 |
53 | 53 | ||
54 | #define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID) | ||
55 | #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) | ||
54 | #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") | 56 | #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") |
55 | 57 | ||
56 | /** | 58 | /** |
57 | * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) | 59 | * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) |
58 | * @pdev: device data pointer | 60 | * @pdev: device data pointer |
59 | * @regmap: SAI register map pointer | 61 | * @regmap: SAI register map pointer |
62 | * @regmap_config: SAI sub block register map configuration pointer | ||
60 | * @dma_params: dma configuration data for rx or tx channel | 63 | * @dma_params: dma configuration data for rx or tx channel |
61 | * @cpu_dai_drv: DAI driver data pointer | 64 | * @cpu_dai_drv: DAI driver data pointer |
62 | * @cpu_dai: DAI runtime data pointer | 65 | * @cpu_dai: DAI runtime data pointer |
@@ -79,6 +82,7 @@ | |||
79 | struct stm32_sai_sub_data { | 82 | struct stm32_sai_sub_data { |
80 | struct platform_device *pdev; | 83 | struct platform_device *pdev; |
81 | struct regmap *regmap; | 84 | struct regmap *regmap; |
85 | const struct regmap_config *regmap_config; | ||
82 | struct snd_dmaengine_dai_dma_data dma_params; | 86 | struct snd_dmaengine_dai_dma_data dma_params; |
83 | struct snd_soc_dai_driver *cpu_dai_drv; | 87 | struct snd_soc_dai_driver *cpu_dai_drv; |
84 | struct snd_soc_dai *cpu_dai; | 88 | struct snd_soc_dai *cpu_dai; |
@@ -118,6 +122,8 @@ static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) | |||
118 | case STM_SAI_SR_REGX: | 122 | case STM_SAI_SR_REGX: |
119 | case STM_SAI_CLRFR_REGX: | 123 | case STM_SAI_CLRFR_REGX: |
120 | case STM_SAI_DR_REGX: | 124 | case STM_SAI_DR_REGX: |
125 | case STM_SAI_PDMCR_REGX: | ||
126 | case STM_SAI_PDMLY_REGX: | ||
121 | return true; | 127 | return true; |
122 | default: | 128 | default: |
123 | return false; | 129 | return false; |
@@ -145,13 +151,15 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) | |||
145 | case STM_SAI_SR_REGX: | 151 | case STM_SAI_SR_REGX: |
146 | case STM_SAI_CLRFR_REGX: | 152 | case STM_SAI_CLRFR_REGX: |
147 | case STM_SAI_DR_REGX: | 153 | case STM_SAI_DR_REGX: |
154 | case STM_SAI_PDMCR_REGX: | ||
155 | case STM_SAI_PDMLY_REGX: | ||
148 | return true; | 156 | return true; |
149 | default: | 157 | default: |
150 | return false; | 158 | return false; |
151 | } | 159 | } |
152 | } | 160 | } |
153 | 161 | ||
154 | static const struct regmap_config stm32_sai_sub_regmap_config = { | 162 | static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { |
155 | .reg_bits = 32, | 163 | .reg_bits = 32, |
156 | .reg_stride = 4, | 164 | .reg_stride = 4, |
157 | .val_bits = 32, | 165 | .val_bits = 32, |
@@ -162,6 +170,17 @@ static const struct regmap_config stm32_sai_sub_regmap_config = { | |||
162 | .fast_io = true, | 170 | .fast_io = true, |
163 | }; | 171 | }; |
164 | 172 | ||
173 | static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { | ||
174 | .reg_bits = 32, | ||
175 | .reg_stride = 4, | ||
176 | .val_bits = 32, | ||
177 | .max_register = STM_SAI_PDMLY_REGX, | ||
178 | .readable_reg = stm32_sai_sub_readable_reg, | ||
179 | .volatile_reg = stm32_sai_sub_volatile_reg, | ||
180 | .writeable_reg = stm32_sai_sub_writeable_reg, | ||
181 | .fast_io = true, | ||
182 | }; | ||
183 | |||
165 | static irqreturn_t stm32_sai_isr(int irq, void *devid) | 184 | static irqreturn_t stm32_sai_isr(int irq, void *devid) |
166 | { | 185 | { |
167 | struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; | 186 | 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, | |||
551 | { | 570 | { |
552 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | 571 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); |
553 | int cr1, mask, div = 0; | 572 | int cr1, mask, div = 0; |
554 | int sai_clk_rate, ret; | 573 | int sai_clk_rate, mclk_ratio, den, ret; |
574 | int version = sai->pdata->conf->version; | ||
555 | 575 | ||
556 | if (!sai->mclk_rate) { | 576 | if (!sai->mclk_rate) { |
557 | dev_err(cpu_dai->dev, "Mclk rate is null\n"); | 577 | dev_err(cpu_dai->dev, "Mclk rate is null\n"); |
@@ -564,22 +584,54 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, | |||
564 | clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); | 584 | clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); |
565 | sai_clk_rate = clk_get_rate(sai->sai_ck); | 585 | sai_clk_rate = clk_get_rate(sai->sai_ck); |
566 | 586 | ||
567 | /* | 587 | if (STM_SAI_IS_F4(sai->pdata)) { |
568 | * mclk_rate = 256 * fs | 588 | /* |
569 | * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate | 589 | * mclk_rate = 256 * fs |
570 | * MCKDIV = sai_ck / (2 * mclk_rate) otherwise | 590 | * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate |
571 | */ | 591 | * MCKDIV = sai_ck / (2 * mclk_rate) otherwise |
572 | if (2 * sai_clk_rate >= 3 * sai->mclk_rate) | 592 | */ |
573 | div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate); | 593 | if (2 * sai_clk_rate >= 3 * sai->mclk_rate) |
574 | 594 | div = DIV_ROUND_CLOSEST(sai_clk_rate, | |
575 | if (div > SAI_XCR1_MCKDIV_MAX) { | 595 | 2 * sai->mclk_rate); |
596 | } else { | ||
597 | /* | ||
598 | * TDM mode : | ||
599 | * mclk on | ||
600 | * MCKDIV = sai_ck / (ws x 256) (NOMCK=0. OSR=0) | ||
601 | * MCKDIV = sai_ck / (ws x 512) (NOMCK=0. OSR=1) | ||
602 | * mclk off | ||
603 | * MCKDIV = sai_ck / (frl x ws) (NOMCK=1) | ||
604 | * Note: NOMCK/NODIV correspond to same bit. | ||
605 | */ | ||
606 | if (sai->mclk_rate) { | ||
607 | mclk_ratio = sai->mclk_rate / params_rate(params); | ||
608 | if (mclk_ratio != 256) { | ||
609 | if (mclk_ratio == 512) { | ||
610 | mask = SAI_XCR1_OSR; | ||
611 | cr1 = SAI_XCR1_OSR; | ||
612 | } else { | ||
613 | dev_err(cpu_dai->dev, | ||
614 | "Wrong mclk ratio %d\n", | ||
615 | mclk_ratio); | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | } | ||
619 | div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate); | ||
620 | } else { | ||
621 | /* mclk-fs not set, master clock not active. NOMCK=1 */ | ||
622 | den = sai->fs_length * params_rate(params); | ||
623 | div = DIV_ROUND_CLOSEST(sai_clk_rate, den); | ||
624 | } | ||
625 | } | ||
626 | |||
627 | if (div > SAI_XCR1_MCKDIV_MAX(version)) { | ||
576 | dev_err(cpu_dai->dev, "Divider %d out of range\n", div); | 628 | dev_err(cpu_dai->dev, "Divider %d out of range\n", div); |
577 | return -EINVAL; | 629 | return -EINVAL; |
578 | } | 630 | } |
579 | dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); | 631 | dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); |
580 | 632 | ||
581 | mask = SAI_XCR1_MCKDIV_MASK; | 633 | mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); |
582 | cr1 = SAI_XCR1_MCKDIV_SET(div); | 634 | cr1 = SAI_XCR1_MCKDIV_SET(div); |
583 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); | 635 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); |
584 | if (ret < 0) { | 636 | if (ret < 0) { |
585 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | 637 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); |
@@ -780,8 +832,18 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, | |||
780 | return PTR_ERR(base); | 832 | return PTR_ERR(base); |
781 | 833 | ||
782 | sai->phys_addr = res->start; | 834 | sai->phys_addr = res->start; |
783 | sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", base, | 835 | |
784 | &stm32_sai_sub_regmap_config); | 836 | sai->regmap_config = &stm32_sai_sub_regmap_config_f4; |
837 | /* Note: PDM registers not available for H7 sub-block B */ | ||
838 | if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai)) | ||
839 | sai->regmap_config = &stm32_sai_sub_regmap_config_h7; | ||
840 | |||
841 | sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", | ||
842 | base, sai->regmap_config); | ||
843 | if (IS_ERR(sai->regmap)) { | ||
844 | dev_err(&pdev->dev, "Failed to initialize MMIO\n"); | ||
845 | return PTR_ERR(sai->regmap); | ||
846 | } | ||
785 | 847 | ||
786 | /* Get direction property */ | 848 | /* Get direction property */ |
787 | if (of_property_match_string(np, "dma-names", "tx") >= 0) { | 849 | if (of_property_match_string(np, "dma-names", "tx") >= 0) { |