diff options
author | Olivier Moysan <olivier.moysan@st.com> | 2017-10-19 09:03:23 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-10-21 06:17:49 -0400 |
commit | 5914d285f6b782892a91d6621723fdc41a775b15 (patch) | |
tree | 2b689ee2ee64790d993f78ce454db48bafca7555 | |
parent | 47a8907d7c73fad81030655f09832fbb4446a2f5 (diff) |
ASoC: stm32: sai: Add synchronization support
Add Synchronization support for STM32 SAI.
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 | 160 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai.h | 22 | ||||
-rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 95 |
3 files changed, 269 insertions, 8 deletions
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 5fe878ace605..d6f71a3406e9 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c | |||
@@ -16,6 +16,7 @@ | |||
16 | * details. | 16 | * details. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/bitfield.h> | ||
19 | #include <linux/clk.h> | 20 | #include <linux/clk.h> |
20 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
21 | #include <linux/module.h> | 22 | #include <linux/module.h> |
@@ -27,6 +28,16 @@ | |||
27 | 28 | ||
28 | #include "stm32_sai.h" | 29 | #include "stm32_sai.h" |
29 | 30 | ||
31 | static LIST_HEAD(sync_providers); | ||
32 | static DEFINE_MUTEX(sync_mutex); | ||
33 | |||
34 | struct sync_provider { | ||
35 | struct list_head link; | ||
36 | struct device_node *node; | ||
37 | int (*sync_conf)(void *data, int synco); | ||
38 | void *data; | ||
39 | }; | ||
40 | |||
30 | static const struct stm32_sai_conf stm32_sai_conf_f4 = { | 41 | static const struct stm32_sai_conf stm32_sai_conf_f4 = { |
31 | .version = SAI_STM32F4, | 42 | .version = SAI_STM32F4, |
32 | }; | 43 | }; |
@@ -41,23 +52,143 @@ static const struct of_device_id stm32_sai_ids[] = { | |||
41 | {} | 52 | {} |
42 | }; | 53 | }; |
43 | 54 | ||
55 | static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) | ||
56 | { | ||
57 | int ret; | ||
58 | |||
59 | /* Enable peripheral clock to allow GCR register access */ | ||
60 | ret = clk_prepare_enable(sai->pclk); | ||
61 | if (ret) { | ||
62 | dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); | ||
63 | return ret; | ||
64 | } | ||
65 | |||
66 | writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); | ||
67 | |||
68 | clk_disable_unprepare(sai->pclk); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int stm32_sai_sync_conf_provider(void *data, int synco) | ||
74 | { | ||
75 | struct stm32_sai_data *sai = (struct stm32_sai_data *)data; | ||
76 | u32 prev_synco; | ||
77 | int ret; | ||
78 | |||
79 | /* Enable peripheral clock to allow GCR register access */ | ||
80 | ret = clk_prepare_enable(sai->pclk); | ||
81 | if (ret) { | ||
82 | dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); | ||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", | ||
87 | sai->pdev->dev.of_node->name, | ||
88 | synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); | ||
89 | |||
90 | prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); | ||
91 | if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { | ||
92 | dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", | ||
93 | sai->pdev->dev.of_node->name, | ||
94 | prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); | ||
95 | clk_disable_unprepare(sai->pclk); | ||
96 | return -EINVAL; | ||
97 | } | ||
98 | |||
99 | writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); | ||
100 | |||
101 | clk_disable_unprepare(sai->pclk); | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static int stm32_sai_set_sync_provider(struct device_node *np, int synco) | ||
107 | { | ||
108 | struct sync_provider *provider; | ||
109 | int ret; | ||
110 | |||
111 | mutex_lock(&sync_mutex); | ||
112 | list_for_each_entry(provider, &sync_providers, link) { | ||
113 | if (provider->node == np) { | ||
114 | ret = provider->sync_conf(provider->data, synco); | ||
115 | mutex_unlock(&sync_mutex); | ||
116 | return ret; | ||
117 | } | ||
118 | } | ||
119 | mutex_unlock(&sync_mutex); | ||
120 | |||
121 | /* SAI sync provider not found */ | ||
122 | return -ENODEV; | ||
123 | } | ||
124 | |||
125 | static int stm32_sai_set_sync(struct stm32_sai_data *sai, | ||
126 | struct device_node *np_provider, | ||
127 | int synco, int synci) | ||
128 | { | ||
129 | int ret; | ||
130 | |||
131 | /* Configure sync client */ | ||
132 | stm32_sai_sync_conf_client(sai, synci); | ||
133 | |||
134 | /* Configure sync provider */ | ||
135 | ret = stm32_sai_set_sync_provider(np_provider, synco); | ||
136 | |||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | static int stm32_sai_sync_add_provider(struct platform_device *pdev, | ||
141 | void *data) | ||
142 | { | ||
143 | struct sync_provider *sp; | ||
144 | |||
145 | sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); | ||
146 | if (!sp) | ||
147 | return -ENOMEM; | ||
148 | |||
149 | sp->node = of_node_get(pdev->dev.of_node); | ||
150 | sp->data = data; | ||
151 | sp->sync_conf = &stm32_sai_sync_conf_provider; | ||
152 | |||
153 | mutex_lock(&sync_mutex); | ||
154 | list_add(&sp->link, &sync_providers); | ||
155 | mutex_unlock(&sync_mutex); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static void stm32_sai_sync_del_provider(struct device_node *np) | ||
161 | { | ||
162 | struct sync_provider *sp; | ||
163 | |||
164 | mutex_lock(&sync_mutex); | ||
165 | list_for_each_entry(sp, &sync_providers, link) { | ||
166 | if (sp->node == np) { | ||
167 | list_del(&sp->link); | ||
168 | of_node_put(sp->node); | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | mutex_unlock(&sync_mutex); | ||
173 | } | ||
174 | |||
44 | static int stm32_sai_probe(struct platform_device *pdev) | 175 | static int stm32_sai_probe(struct platform_device *pdev) |
45 | { | 176 | { |
46 | struct device_node *np = pdev->dev.of_node; | 177 | struct device_node *np = pdev->dev.of_node; |
47 | struct stm32_sai_data *sai; | 178 | struct stm32_sai_data *sai; |
48 | struct reset_control *rst; | 179 | struct reset_control *rst; |
49 | struct resource *res; | 180 | struct resource *res; |
50 | void __iomem *base; | ||
51 | const struct of_device_id *of_id; | 181 | const struct of_device_id *of_id; |
182 | int ret; | ||
52 | 183 | ||
53 | sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); | 184 | sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); |
54 | if (!sai) | 185 | if (!sai) |
55 | return -ENOMEM; | 186 | return -ENOMEM; |
56 | 187 | ||
57 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 188 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
58 | base = devm_ioremap_resource(&pdev->dev, res); | 189 | sai->base = devm_ioremap_resource(&pdev->dev, res); |
59 | if (IS_ERR(base)) | 190 | if (IS_ERR(sai->base)) |
60 | return PTR_ERR(base); | 191 | return PTR_ERR(sai->base); |
61 | 192 | ||
62 | of_id = of_match_device(stm32_sai_ids, &pdev->dev); | 193 | of_id = of_match_device(stm32_sai_ids, &pdev->dev); |
63 | if (of_id) | 194 | if (of_id) |
@@ -65,6 +196,14 @@ static int stm32_sai_probe(struct platform_device *pdev) | |||
65 | else | 196 | else |
66 | return -EINVAL; | 197 | return -EINVAL; |
67 | 198 | ||
199 | if (!STM_SAI_IS_F4(sai)) { | ||
200 | sai->pclk = devm_clk_get(&pdev->dev, "pclk"); | ||
201 | if (IS_ERR(sai->pclk)) { | ||
202 | dev_err(&pdev->dev, "missing bus clock pclk\n"); | ||
203 | return PTR_ERR(sai->pclk); | ||
204 | } | ||
205 | } | ||
206 | |||
68 | sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); | 207 | sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); |
69 | if (IS_ERR(sai->clk_x8k)) { | 208 | if (IS_ERR(sai->clk_x8k)) { |
70 | dev_err(&pdev->dev, "missing x8k parent clock\n"); | 209 | dev_err(&pdev->dev, "missing x8k parent clock\n"); |
@@ -92,16 +231,27 @@ static int stm32_sai_probe(struct platform_device *pdev) | |||
92 | reset_control_deassert(rst); | 231 | reset_control_deassert(rst); |
93 | } | 232 | } |
94 | 233 | ||
234 | ret = stm32_sai_sync_add_provider(pdev, sai); | ||
235 | if (ret < 0) | ||
236 | return ret; | ||
237 | sai->set_sync = &stm32_sai_set_sync; | ||
238 | |||
95 | sai->pdev = pdev; | 239 | sai->pdev = pdev; |
96 | platform_set_drvdata(pdev, sai); | 240 | platform_set_drvdata(pdev, sai); |
97 | 241 | ||
98 | return of_platform_populate(np, NULL, NULL, &pdev->dev); | 242 | ret = of_platform_populate(np, NULL, NULL, &pdev->dev); |
243 | if (ret < 0) | ||
244 | stm32_sai_sync_del_provider(np); | ||
245 | |||
246 | return ret; | ||
99 | } | 247 | } |
100 | 248 | ||
101 | static int stm32_sai_remove(struct platform_device *pdev) | 249 | static int stm32_sai_remove(struct platform_device *pdev) |
102 | { | 250 | { |
103 | of_platform_depopulate(&pdev->dev); | 251 | of_platform_depopulate(&pdev->dev); |
104 | 252 | ||
253 | stm32_sai_sync_del_provider(pdev->dev.of_node); | ||
254 | |||
105 | return 0; | 255 | return 0; |
106 | } | 256 | } |
107 | 257 | ||
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index 889974dc62d9..bb062e70de63 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h | |||
@@ -16,9 +16,11 @@ | |||
16 | * details. | 16 | * details. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/bitfield.h> | ||
20 | |||
19 | /******************** SAI Register Map **************************************/ | 21 | /******************** SAI Register Map **************************************/ |
20 | 22 | ||
21 | /* common register */ | 23 | /* Global configuration register */ |
22 | #define STM_SAI_GCR 0x00 | 24 | #define STM_SAI_GCR 0x00 |
23 | 25 | ||
24 | /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ | 26 | /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ |
@@ -37,12 +39,13 @@ | |||
37 | 39 | ||
38 | /******************** Bit definition for SAI_GCR register *******************/ | 40 | /******************** Bit definition for SAI_GCR register *******************/ |
39 | #define SAI_GCR_SYNCIN_SHIFT 0 | 41 | #define SAI_GCR_SYNCIN_SHIFT 0 |
42 | #define SAI_GCR_SYNCIN_WDTH 2 | ||
40 | #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) | 43 | #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) |
41 | #define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) | 44 | #define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\ |
45 | SAI_GCR_SYNCIN_MASK) | ||
42 | 46 | ||
43 | #define SAI_GCR_SYNCOUT_SHIFT 4 | 47 | #define SAI_GCR_SYNCOUT_SHIFT 4 |
44 | #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) | 48 | #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) |
45 | #define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) | ||
46 | 49 | ||
47 | /******************* Bit definition for SAI_XCR1 register *******************/ | 50 | /******************* Bit definition for SAI_XCR1 register *******************/ |
48 | #define SAI_XCR1_RX_TX_SHIFT 0 | 51 | #define SAI_XCR1_RX_TX_SHIFT 0 |
@@ -231,6 +234,12 @@ | |||
231 | #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) | 234 | #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) |
232 | #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) | 235 | #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) |
233 | 236 | ||
237 | enum stm32_sai_syncout { | ||
238 | STM_SAI_SYNC_OUT_NONE, | ||
239 | STM_SAI_SYNC_OUT_A, | ||
240 | STM_SAI_SYNC_OUT_B, | ||
241 | }; | ||
242 | |||
234 | enum stm32_sai_version { | 243 | enum stm32_sai_version { |
235 | SAI_STM32F4, | 244 | SAI_STM32F4, |
236 | SAI_STM32H7 | 245 | SAI_STM32H7 |
@@ -247,15 +256,22 @@ struct stm32_sai_conf { | |||
247 | /** | 256 | /** |
248 | * struct stm32_sai_data - private data of SAI instance driver | 257 | * struct stm32_sai_data - private data of SAI instance driver |
249 | * @pdev: device data pointer | 258 | * @pdev: device data pointer |
259 | * @base: common register bank virtual base address | ||
260 | * @pclk: SAI bus clock | ||
250 | * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz | 261 | * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz |
251 | * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz | 262 | * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz |
252 | * @version: SOC version | 263 | * @version: SOC version |
253 | * @irq: SAI interrupt line | 264 | * @irq: SAI interrupt line |
265 | * @set_sync: pointer to synchro mode configuration callback | ||
254 | */ | 266 | */ |
255 | struct stm32_sai_data { | 267 | struct stm32_sai_data { |
256 | struct platform_device *pdev; | 268 | struct platform_device *pdev; |
269 | void __iomem *base; | ||
270 | struct clk *pclk; | ||
257 | struct clk *clk_x8k; | 271 | struct clk *clk_x8k; |
258 | struct clk *clk_x11k; | 272 | struct clk *clk_x11k; |
259 | struct stm32_sai_conf *conf; | 273 | struct stm32_sai_conf *conf; |
260 | int irq; | 274 | int irq; |
275 | int (*set_sync)(struct stm32_sai_data *sai, | ||
276 | struct device_node *np_provider, int synco, int synci); | ||
261 | }; | 277 | }; |
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index fd7dc7760f58..150ad546d8b9 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c | |||
@@ -55,6 +55,12 @@ | |||
55 | #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) | 55 | #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) |
56 | #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") |
57 | 57 | ||
58 | #define SAI_SYNC_NONE 0x0 | ||
59 | #define SAI_SYNC_INTERNAL 0x1 | ||
60 | #define SAI_SYNC_EXTERNAL 0x2 | ||
61 | |||
62 | #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) | ||
63 | |||
58 | /** | 64 | /** |
59 | * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) | 65 | * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) |
60 | * @pdev: device data pointer | 66 | * @pdev: device data pointer |
@@ -65,6 +71,7 @@ | |||
65 | * @cpu_dai: DAI runtime data pointer | 71 | * @cpu_dai: DAI runtime data pointer |
66 | * @substream: PCM substream data pointer | 72 | * @substream: PCM substream data pointer |
67 | * @pdata: SAI block parent data pointer | 73 | * @pdata: SAI block parent data pointer |
74 | * @np_sync_provider: synchronization provider node | ||
68 | * @sai_ck: kernel clock feeding the SAI clock generator | 75 | * @sai_ck: kernel clock feeding the SAI clock generator |
69 | * @phys_addr: SAI registers physical base address | 76 | * @phys_addr: SAI registers physical base address |
70 | * @mclk_rate: SAI block master clock frequency (Hz). set at init | 77 | * @mclk_rate: SAI block master clock frequency (Hz). set at init |
@@ -73,6 +80,8 @@ | |||
73 | * @master: SAI block mode flag. (true=master, false=slave) set at init | 80 | * @master: SAI block mode flag. (true=master, false=slave) set at init |
74 | * @fmt: SAI block format. relevant only for custom protocols. set at init | 81 | * @fmt: SAI block format. relevant only for custom protocols. set at init |
75 | * @sync: SAI block synchronization mode. (none, internal or external) | 82 | * @sync: SAI block synchronization mode. (none, internal or external) |
83 | * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) | ||
84 | * @synci: SAI block ext sync source (client setting). (SAI sync provider index) | ||
76 | * @fs_length: frame synchronization length. depends on protocol settings | 85 | * @fs_length: frame synchronization length. depends on protocol settings |
77 | * @slots: rx or tx slot number | 86 | * @slots: rx or tx slot number |
78 | * @slot_width: rx or tx slot width in bits | 87 | * @slot_width: rx or tx slot width in bits |
@@ -88,6 +97,7 @@ struct stm32_sai_sub_data { | |||
88 | struct snd_soc_dai *cpu_dai; | 97 | struct snd_soc_dai *cpu_dai; |
89 | struct snd_pcm_substream *substream; | 98 | struct snd_pcm_substream *substream; |
90 | struct stm32_sai_data *pdata; | 99 | struct stm32_sai_data *pdata; |
100 | struct device_node *np_sync_provider; | ||
91 | struct clk *sai_ck; | 101 | struct clk *sai_ck; |
92 | dma_addr_t phys_addr; | 102 | dma_addr_t phys_addr; |
93 | unsigned int mclk_rate; | 103 | unsigned int mclk_rate; |
@@ -96,6 +106,8 @@ struct stm32_sai_sub_data { | |||
96 | bool master; | 106 | bool master; |
97 | int fmt; | 107 | int fmt; |
98 | int sync; | 108 | int sync; |
109 | int synco; | ||
110 | int synci; | ||
99 | int fs_length; | 111 | int fs_length; |
100 | int slots; | 112 | int slots; |
101 | int slot_width; | 113 | int slot_width; |
@@ -387,6 +399,14 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |||
387 | fmt & SND_SOC_DAIFMT_MASTER_MASK); | 399 | fmt & SND_SOC_DAIFMT_MASTER_MASK); |
388 | return -EINVAL; | 400 | return -EINVAL; |
389 | } | 401 | } |
402 | |||
403 | /* Set slave mode if sub-block is synchronized with another SAI */ | ||
404 | if (sai->sync) { | ||
405 | dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); | ||
406 | cr1 |= SAI_XCR1_SLAVE; | ||
407 | sai->master = false; | ||
408 | } | ||
409 | |||
390 | cr1_mask |= SAI_XCR1_SLAVE; | 410 | cr1_mask |= SAI_XCR1_SLAVE; |
391 | 411 | ||
392 | /* do not generate master by default */ | 412 | /* do not generate master by default */ |
@@ -749,6 +769,16 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) | |||
749 | if (STM_SAI_IS_CAPTURE(sai)) | 769 | if (STM_SAI_IS_CAPTURE(sai)) |
750 | cr1 |= SAI_XCR1_RX_TX; | 770 | cr1 |= SAI_XCR1_RX_TX; |
751 | 771 | ||
772 | /* Configure synchronization */ | ||
773 | if (sai->sync == SAI_SYNC_EXTERNAL) { | ||
774 | /* Configure synchro client and provider */ | ||
775 | sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, | ||
776 | sai->synco, sai->synci); | ||
777 | } | ||
778 | |||
779 | cr1_mask |= SAI_XCR1_SYNCEN_MASK; | ||
780 | cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); | ||
781 | |||
752 | return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); | 782 | return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); |
753 | } | 783 | } |
754 | 784 | ||
@@ -835,6 +865,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, | |||
835 | struct device_node *np = pdev->dev.of_node; | 865 | struct device_node *np = pdev->dev.of_node; |
836 | struct resource *res; | 866 | struct resource *res; |
837 | void __iomem *base; | 867 | void __iomem *base; |
868 | struct of_phandle_args args; | ||
869 | int ret; | ||
838 | 870 | ||
839 | if (!np) | 871 | if (!np) |
840 | return -ENODEV; | 872 | return -ENODEV; |
@@ -868,6 +900,69 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, | |||
868 | return -EINVAL; | 900 | return -EINVAL; |
869 | } | 901 | } |
870 | 902 | ||
903 | /* Get synchronization property */ | ||
904 | args.np = NULL; | ||
905 | ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); | ||
906 | if (ret < 0 && ret != -ENOENT) { | ||
907 | dev_err(&pdev->dev, "Failed to get st,sync property\n"); | ||
908 | return ret; | ||
909 | } | ||
910 | |||
911 | sai->sync = SAI_SYNC_NONE; | ||
912 | if (args.np) { | ||
913 | if (args.np == np) { | ||
914 | dev_err(&pdev->dev, "%s sync own reference\n", | ||
915 | np->name); | ||
916 | of_node_put(args.np); | ||
917 | return -EINVAL; | ||
918 | } | ||
919 | |||
920 | sai->np_sync_provider = of_get_parent(args.np); | ||
921 | if (!sai->np_sync_provider) { | ||
922 | dev_err(&pdev->dev, "%s parent node not found\n", | ||
923 | np->name); | ||
924 | of_node_put(args.np); | ||
925 | return -ENODEV; | ||
926 | } | ||
927 | |||
928 | sai->sync = SAI_SYNC_INTERNAL; | ||
929 | if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { | ||
930 | if (!STM_SAI_HAS_EXT_SYNC(sai)) { | ||
931 | dev_err(&pdev->dev, | ||
932 | "External synchro not supported\n"); | ||
933 | of_node_put(args.np); | ||
934 | return -EINVAL; | ||
935 | } | ||
936 | sai->sync = SAI_SYNC_EXTERNAL; | ||
937 | |||
938 | sai->synci = args.args[0]; | ||
939 | if (sai->synci < 1 || | ||
940 | (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { | ||
941 | dev_err(&pdev->dev, "Wrong SAI index\n"); | ||
942 | of_node_put(args.np); | ||
943 | return -EINVAL; | ||
944 | } | ||
945 | |||
946 | if (of_property_match_string(args.np, "compatible", | ||
947 | "st,stm32-sai-sub-a") >= 0) | ||
948 | sai->synco = STM_SAI_SYNC_OUT_A; | ||
949 | |||
950 | if (of_property_match_string(args.np, "compatible", | ||
951 | "st,stm32-sai-sub-b") >= 0) | ||
952 | sai->synco = STM_SAI_SYNC_OUT_B; | ||
953 | |||
954 | if (!sai->synco) { | ||
955 | dev_err(&pdev->dev, "Unknown SAI sub-block\n"); | ||
956 | of_node_put(args.np); | ||
957 | return -EINVAL; | ||
958 | } | ||
959 | } | ||
960 | |||
961 | dev_dbg(&pdev->dev, "%s synchronized with %s\n", | ||
962 | pdev->name, args.np->full_name); | ||
963 | } | ||
964 | |||
965 | of_node_put(args.np); | ||
871 | sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); | 966 | sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); |
872 | if (IS_ERR(sai->sai_ck)) { | 967 | if (IS_ERR(sai->sai_ck)) { |
873 | dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); | 968 | dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); |