diff options
author | Misael Lopez Cruz | 2013-06-16 18:33:50 -0500 |
---|---|---|
committer | Misael Lopez Cruz | 2013-07-22 15:04:21 -0500 |
commit | 9dabab86b73716ba98e4a7cc4516e85a1d5e8507 (patch) | |
tree | 63871bc52d53691c4153cdf233b29a02d65d63db | |
parent | a0d1af7abccce43ef3f1a5d3d585344367ecc89d (diff) | |
download | kernel-audio-9dabab86b73716ba98e4a7cc4516e85a1d5e8507.tar.gz kernel-audio-9dabab86b73716ba98e4a7cc4516e85a1d5e8507.tar.xz kernel-audio-9dabab86b73716ba98e4a7cc4516e85a1d5e8507.zip |
ASoC: DRA7: atl: Add initial support for Audio Tracking Logic
Add initial version of Audio Tracking Logic (ATL) driver. ATL is
used to synchronize the digital audio output to the baseband clock.
ATL produces a timing signal at the top of the audio clock tree.
Change-Id: I123ff440b8e478c12e28ff4db42d1196f0ae4f6b
Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
-rw-r--r-- | Documentation/devicetree/bindings/sound/dra7-atl.txt | 42 | ||||
-rw-r--r-- | sound/soc/omap/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/omap/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/omap/dra7-atl.c | 317 |
4 files changed, 364 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/dra7-atl.txt b/Documentation/devicetree/bindings/sound/dra7-atl.txt new file mode 100644 index 000000000000..cd85aa3477cd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/dra7-atl.txt | |||
@@ -0,0 +1,42 @@ | |||
1 | * Texas Instruments DRA7 Audio Tracking Logic (ATL) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "ti,dra7-atl" | ||
5 | - ti,hwmods: Name of the hwmod associated with the ATL module | ||
6 | |||
7 | |||
8 | Optional properties: | ||
9 | - ti,atclk<n>-freq: Output clock frequency for ATL instance n. | ||
10 | ATL instance is disabled if this property is not set | ||
11 | - ti,atl<n>-bws-input: ATL baseband word select input for ATL instance n. | ||
12 | Possible inputs are: | ||
13 | 0 - McASP1 FSR | ||
14 | 1 - McASP1 FSX | ||
15 | 2 - McASP2 FSR | ||
16 | 3 - McASP2 FSX | ||
17 | 4 - McASP3 FSX | ||
18 | 5 - McASP4 FSX | ||
19 | 6 - McASP5 FSX | ||
20 | 7 - McASP6 FSX | ||
21 | 8 - McASP7 FSX | ||
22 | 9 - McASP8 FSX | ||
23 | 10 - McASP8 AHCLKX | ||
24 | 11 - XREF_CLK3 input pad | ||
25 | 12 - XREF_CLK0 input pad | ||
26 | 13 - XREF_CLK1 input pad | ||
27 | 14 - XREF_CLK2 input pad | ||
28 | 15 - OSC1_X1 input pad | ||
29 | - ti,atl<n>-aws-input: Audio word select input for ATL instance n. | ||
30 | Same inputs than BWS. | ||
31 | |||
32 | |||
33 | Example: | ||
34 | |||
35 | atl: atl@0x4843c000 { | ||
36 | compatible = "ti,dra7-atl"; | ||
37 | reg = <0x4843c000 0x3ff>; | ||
38 | ti,hwmods = "atl"; | ||
39 | ti,atclk1-freq = <11289600>; /* 11.2896 MHz */ | ||
40 | ti,atl1-bws-input = <2>; /* McASP2 FSR */ | ||
41 | ti,atl1-aws-input = <4>; /* McASP3 FSX */ | ||
42 | }; | ||
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 9afb392e87e8..31b6aa320f5b 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -130,3 +130,6 @@ config SND_OMAP_SOC_OMAP3_PANDORA | |||
130 | select SND_SOC_TWL4030 | 130 | select SND_SOC_TWL4030 |
131 | help | 131 | help |
132 | Say Y if you want to add support for SoC audio on the OMAP3 Pandora. | 132 | Say Y if you want to add support for SoC audio on the OMAP3 Pandora. |
133 | |||
134 | config SND_DRA7_SOC_ATL | ||
135 | tristate | ||
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 4e89a7fd3e8f..ff334799e662 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -8,6 +8,7 @@ snd-soc-omap-hdmi-objs := omap-hdmi.o | |||
8 | snd-soc-omap-abe-objs := omap-abe-core.o omap-abe-dbg.o omap-abe-mixer.o \ | 8 | snd-soc-omap-abe-objs := omap-abe-core.o omap-abe-dbg.o omap-abe-mixer.o \ |
9 | omap-abe-mmap.o omap-abe-opp.o omap-abe-pcm.o \ | 9 | omap-abe-mmap.o omap-abe-opp.o omap-abe-pcm.o \ |
10 | omap-abe-pm.o | 10 | omap-abe-pm.o |
11 | snd-soc-dra7-atl-objs := dra7-atl.o | ||
11 | 12 | ||
12 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o | 13 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o |
13 | obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o | 14 | obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o |
@@ -16,6 +17,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | |||
16 | obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o | 17 | obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o |
17 | obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o | 18 | obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o |
18 | obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o aess/ | 19 | obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o aess/ |
20 | obj-$(CONFIG_SND_DRA7_SOC_ATL) += snd-soc-dra7-atl.o | ||
19 | 21 | ||
20 | # OMAP Machine Support | 22 | # OMAP Machine Support |
21 | snd-soc-n810-objs := n810.o | 23 | snd-soc-n810-objs := n810.o |
diff --git a/sound/soc/omap/dra7-atl.c b/sound/soc/omap/dra7-atl.c new file mode 100644 index 000000000000..156dc29c9602 --- /dev/null +++ b/sound/soc/omap/dra7-atl.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | * dra7-atl.c -- DRA7xx Audio Tracking Logic driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * | ||
6 | * Author: Misael Lopez Cruz <misael.lopez@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/err.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <linux/of.h> | ||
31 | #include <linux/clk.h> | ||
32 | #include <linux/pm_runtime.h> | ||
33 | |||
34 | /* ATL instances */ | ||
35 | #define ATL0 0 | ||
36 | #define ATL1 1 | ||
37 | #define ATL2 2 | ||
38 | #define ATL3 3 | ||
39 | #define ATL_INSTANCES 4 | ||
40 | |||
41 | /* ATL instance offsets */ | ||
42 | #define ATL_INST(id) (0x200 + (id * 0x80)) | ||
43 | |||
44 | /* ATL registers */ | ||
45 | #define ATL_REVID 0x000 | ||
46 | #define ATL_PPMR(inst) (inst + 0x00) | ||
47 | #define ATL_BBSR(inst) (inst + 0x04) | ||
48 | #define ATL_ATLCR(inst) (inst + 0x08) | ||
49 | #define ATL_SWEN(inst) (inst + 0x10) | ||
50 | #define ATL_BWSMUX(inst) (inst + 0x14) | ||
51 | #define ATL_AWSMUX(inst) (inst + 0x18) | ||
52 | #define ATL_PCLKMUX(inst) (inst + 0x1c) | ||
53 | |||
54 | /* ATL_ATCLCR */ | ||
55 | #define INTERNAL_DIVIDER_MAX 0x1F | ||
56 | |||
57 | /* ATL_SWEN register */ | ||
58 | #define ATL_DISABLE 0 | ||
59 | #define ATL_ENABLE 1 | ||
60 | |||
61 | /* ATL_BWSMUX / ATL_AWSMUX register */ | ||
62 | #define ATL_WSMUX_MAX 0xF | ||
63 | |||
64 | /* ATL_PCLKMUX register */ | ||
65 | #define PCLKMUX_OCP_CLK 0 | ||
66 | #define PCLKMUX_ATLPCLK 1 | ||
67 | |||
68 | struct atl_data { | ||
69 | struct device *dev; | ||
70 | void __iomem *io_base; | ||
71 | unsigned int atlpclk_freq; | ||
72 | }; | ||
73 | |||
74 | static inline void dra7_atl_write(struct atl_data *atl, u32 reg, u32 val) | ||
75 | { | ||
76 | __raw_writel(val, atl->io_base + reg); | ||
77 | } | ||
78 | |||
79 | static inline int dra7_atl_read(struct atl_data *atl, u32 reg) | ||
80 | { | ||
81 | return __raw_readl(atl->io_base + reg); | ||
82 | } | ||
83 | |||
84 | static int dra7_atl_init(struct atl_data *atl) | ||
85 | { | ||
86 | struct clk *gfclk, *parent_clk; | ||
87 | int ret; | ||
88 | |||
89 | gfclk = clk_get(atl->dev, "atl_gfclk_mux"); | ||
90 | if (IS_ERR(gfclk)) { | ||
91 | dev_err(atl->dev, "failed to get ATLPCLK\n"); | ||
92 | return PTR_ERR(gfclk); | ||
93 | } | ||
94 | |||
95 | parent_clk = clk_get(atl->dev, "dpll_abe_m2_ck"); | ||
96 | if (IS_ERR(parent_clk)) { | ||
97 | dev_err(atl->dev, "failed to get new parent clock\n"); | ||
98 | ret = PTR_ERR(parent_clk); | ||
99 | goto err1; | ||
100 | } | ||
101 | |||
102 | ret = clk_set_parent(gfclk, parent_clk); | ||
103 | if (ret) { | ||
104 | dev_err(atl->dev, "failed to reparent ATLPCLK\n"); | ||
105 | goto err2; | ||
106 | } | ||
107 | |||
108 | atl->atlpclk_freq = clk_get_rate(gfclk); | ||
109 | dev_dbg(atl->dev, "ATLPCLK is at %u Hz\n", atl->atlpclk_freq); | ||
110 | |||
111 | err2: | ||
112 | clk_put(parent_clk); | ||
113 | err1: | ||
114 | clk_put(gfclk); | ||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | static void dra7_atl_dump(struct atl_data *atl, int id) | ||
119 | { | ||
120 | int inst = ATL_INST(id); | ||
121 | |||
122 | dev_dbg(atl->dev, "ATL_PPMR%d = 0x%08x\n", | ||
123 | id, dra7_atl_read(atl, ATL_PPMR(inst))); | ||
124 | dev_dbg(atl->dev, "ATL_BBSR%d = 0x%08x\n", | ||
125 | id, dra7_atl_read(atl, ATL_BBSR(inst))); | ||
126 | dev_dbg(atl->dev, "ATL_ATLCR%d = 0x%08x\n", | ||
127 | id, dra7_atl_read(atl, ATL_ATLCR(inst))); | ||
128 | dev_dbg(atl->dev, "ATL_SWEN%d = 0x%08x\n", | ||
129 | id, dra7_atl_read(atl, ATL_SWEN(inst))); | ||
130 | dev_dbg(atl->dev, "ATL_BWSMUX%d = 0x%08x\n", | ||
131 | id, dra7_atl_read(atl, ATL_BWSMUX(inst))); | ||
132 | dev_dbg(atl->dev, "ATL_AWSMUX%d = 0x%08x\n", | ||
133 | id, dra7_atl_read(atl, ATL_AWSMUX(inst))); | ||
134 | dev_dbg(atl->dev, "ATL_PCLKMUX%d = 0x%08x\n", | ||
135 | id, dra7_atl_read(atl, ATL_PCLKMUX(inst))); | ||
136 | } | ||
137 | |||
138 | static int dra7_atl_configure(struct atl_data *atl, int id, | ||
139 | unsigned int bws, unsigned int aws, | ||
140 | unsigned int atclk_freq) | ||
141 | { | ||
142 | int inst = ATL_INST(id); | ||
143 | u32 divider; | ||
144 | unsigned int min_freq, max_freq; | ||
145 | |||
146 | if ((bws > ATL_WSMUX_MAX) || (aws > ATL_WSMUX_MAX)) { | ||
147 | dev_err(atl->dev, "invalid word select inputs bws %u aws %u\n", | ||
148 | bws, aws); | ||
149 | return -EINVAL; | ||
150 | } | ||
151 | |||
152 | /* Keep ATL instance disabled */ | ||
153 | if (atclk_freq == 0) | ||
154 | return 0; | ||
155 | |||
156 | /* | ||
157 | * Internal divider cannot be 0 (divide-by-1), so ATCLK max freq | ||
158 | * cannot be higher than ATLPCLK / 2. Max divider allowed is 32 | ||
159 | */ | ||
160 | min_freq = atl->atlpclk_freq / (INTERNAL_DIVIDER_MAX + 1); | ||
161 | max_freq = atl->atlpclk_freq / 2; | ||
162 | |||
163 | dev_dbg(atl->dev, "configure ATL%d to %u Hz\n", id, atclk_freq); | ||
164 | |||
165 | if ((atclk_freq < min_freq) || (atclk_freq > max_freq)) { | ||
166 | dev_err(atl->dev, "requested freq %u is not allowed\n", | ||
167 | atclk_freq); | ||
168 | return -EINVAL; | ||
169 | } | ||
170 | |||
171 | /* Disable ATL while reparenting and changing ATLPCLK input */ | ||
172 | dra7_atl_write(atl, ATL_SWEN(inst), ATL_DISABLE); | ||
173 | |||
174 | /* ATL divider (ATL_INTERNAL_DIVIDER): ATLPCLK-to-ATCLK ratio - 1 */ | ||
175 | divider = (atl->atlpclk_freq / atclk_freq) - 1; | ||
176 | dra7_atl_write(atl, ATL_ATLCR(inst), divider); | ||
177 | |||
178 | /* Enable ATL */ | ||
179 | dra7_atl_write(atl, ATL_SWEN(inst), ATL_ENABLE); | ||
180 | |||
181 | /* Basebased word select */ | ||
182 | dra7_atl_write(atl, ATL_BWSMUX(inst), bws); | ||
183 | |||
184 | /* Audio word select */ | ||
185 | dra7_atl_write(atl, ATL_AWSMUX(inst), aws); | ||
186 | |||
187 | dra7_atl_dump(atl, id); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static void dra7_atl_reset(struct atl_data *atl, int id) | ||
193 | { | ||
194 | dev_dbg(atl->dev, "reset ATL%d\n", id); | ||
195 | dra7_atl_write(atl, ATL_SWEN(ATL_INST(id)), ATL_DISABLE); | ||
196 | } | ||
197 | |||
198 | static int dra7_atl_probe(struct platform_device *pdev) | ||
199 | { | ||
200 | struct device_node *node = pdev->dev.of_node; | ||
201 | struct atl_data *atl; | ||
202 | struct resource *res; | ||
203 | unsigned int atclk_freq; | ||
204 | unsigned int bws, aws; | ||
205 | char prop[128]; | ||
206 | int i; | ||
207 | int ret; | ||
208 | |||
209 | atl = devm_kzalloc(&pdev->dev, sizeof(*atl), GFP_KERNEL); | ||
210 | if (!atl) | ||
211 | return -ENOMEM; | ||
212 | |||
213 | platform_set_drvdata(pdev, atl); | ||
214 | atl->dev = &pdev->dev; | ||
215 | |||
216 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
217 | if (!res) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | if (!devm_request_mem_region(&pdev->dev, res->start, | ||
221 | resource_size(res), "atl")) | ||
222 | return -EBUSY; | ||
223 | |||
224 | atl->io_base = devm_ioremap(&pdev->dev, res->start, | ||
225 | resource_size(res)); | ||
226 | if (!atl->io_base) | ||
227 | return -ENOMEM; | ||
228 | |||
229 | if (!node) { | ||
230 | dev_err(atl->dev, "missing of_node\n"); | ||
231 | return -ENODEV; | ||
232 | } | ||
233 | |||
234 | ret = dra7_atl_init(atl); | ||
235 | if (ret) { | ||
236 | dev_err(atl->dev, "failed to initialize ATL\n"); | ||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | pm_runtime_enable(atl->dev); | ||
241 | pm_runtime_get_sync(atl->dev); | ||
242 | |||
243 | /* | ||
244 | * There is a single ATLPCLK mux for all ATL instances and | ||
245 | * is controlled by PCLKMUX0. The rest of PCLKMUXs don't have | ||
246 | * any effect of clock selection | ||
247 | */ | ||
248 | dra7_atl_write(atl, ATL_PCLKMUX(ATL_INST(0)), PCLKMUX_ATLPCLK); | ||
249 | |||
250 | for (i = 0; i < ATL_INSTANCES; i++) { | ||
251 | atclk_freq = 0; | ||
252 | snprintf(prop, sizeof(prop), "ti,atclk%u-freq", i); | ||
253 | of_property_read_u32(node, prop, &atclk_freq); | ||
254 | |||
255 | bws = 0; | ||
256 | snprintf(prop, sizeof(prop), "ti,atl%u-bws-input", i); | ||
257 | of_property_read_u32(node, prop, &bws); | ||
258 | |||
259 | aws = 0; | ||
260 | snprintf(prop, sizeof(prop), "ti,atl%u-aws-input", i); | ||
261 | of_property_read_u32(node, prop, &aws); | ||
262 | |||
263 | ret = dra7_atl_configure(atl, i, bws, aws, atclk_freq); | ||
264 | if (ret) { | ||
265 | dev_err(atl->dev, "failed to configure ATL%d\n", i); | ||
266 | goto err; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | return 0; | ||
271 | |||
272 | err: | ||
273 | for (i--; i >= 0; --i) | ||
274 | dra7_atl_reset(atl, i); | ||
275 | |||
276 | pm_runtime_put_sync(atl->dev); | ||
277 | pm_runtime_disable(atl->dev); | ||
278 | |||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | static int dra7_atl_remove(struct platform_device *pdev) | ||
283 | { | ||
284 | struct atl_data *atl = platform_get_drvdata(pdev); | ||
285 | int i; | ||
286 | |||
287 | for (i = 0; i < ATL_INSTANCES; i++) | ||
288 | dra7_atl_reset(atl, i); | ||
289 | |||
290 | pm_runtime_put_sync(atl->dev); | ||
291 | pm_runtime_disable(atl->dev); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static const struct of_device_id dra7_atl_of_match[] = { | ||
297 | {.compatible = "ti,dra7-atl", }, | ||
298 | { }, | ||
299 | }; | ||
300 | MODULE_DEVICE_TABLE(of, dra7_atl_of_match); | ||
301 | |||
302 | static struct platform_driver dra7_atl_driver = { | ||
303 | .driver = { | ||
304 | .name = "dra7-atl-sound", | ||
305 | .owner = THIS_MODULE, | ||
306 | .of_match_table = dra7_atl_of_match, | ||
307 | }, | ||
308 | .probe = dra7_atl_probe, | ||
309 | .remove = dra7_atl_remove, | ||
310 | }; | ||
311 | |||
312 | module_platform_driver(dra7_atl_driver); | ||
313 | |||
314 | MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); | ||
315 | MODULE_DESCRIPTION("ATL"); | ||
316 | MODULE_LICENSE("GPL"); | ||
317 | MODULE_ALIAS("platform:dra7-atl"); | ||