diff options
-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 00000000000..cd85aa3477c --- /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 9afb392e87e..31b6aa320f5 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 4e89a7fd3e8..ff334799e66 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 00000000000..156dc29c960 --- /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"); | ||