aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/dra7-atl.txt42
-rw-r--r--sound/soc/omap/Kconfig3
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/dra7-atl.c317
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
3Required properties:
4- compatible: "ti,dra7-atl"
5- ti,hwmods: Name of the hwmod associated with the ATL module
6
7
8Optional 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
33Example:
34
35atl: 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
134config 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
8snd-soc-omap-abe-objs := omap-abe-core.o omap-abe-dbg.o omap-abe-mixer.o \ 8snd-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
11snd-soc-dra7-atl-objs := dra7-atl.o
11 12
12obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o 13obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
13obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o 14obj-$(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
16obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o 17obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
17obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o 18obj-$(CONFIG_SND_OMAP_SOC_HDMI) += snd-soc-omap-hdmi.o
18obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o aess/ 19obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o aess/
20obj-$(CONFIG_SND_DRA7_SOC_ATL) += snd-soc-dra7-atl.o
19 21
20# OMAP Machine Support 22# OMAP Machine Support
21snd-soc-n810-objs := n810.o 23snd-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
68struct atl_data {
69 struct device *dev;
70 void __iomem *io_base;
71 unsigned int atlpclk_freq;
72};
73
74static inline void dra7_atl_write(struct atl_data *atl, u32 reg, u32 val)
75{
76 __raw_writel(val, atl->io_base + reg);
77}
78
79static inline int dra7_atl_read(struct atl_data *atl, u32 reg)
80{
81 return __raw_readl(atl->io_base + reg);
82}
83
84static 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
111err2:
112 clk_put(parent_clk);
113err1:
114 clk_put(gfclk);
115 return ret;
116}
117
118static 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
138static 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
192static 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
198static 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
272err:
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
282static 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
296static const struct of_device_id dra7_atl_of_match[] = {
297 {.compatible = "ti,dra7-atl", },
298 { },
299};
300MODULE_DEVICE_TABLE(of, dra7_atl_of_match);
301
302static 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
312module_platform_driver(dra7_atl_driver);
313
314MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
315MODULE_DESCRIPTION("ATL");
316MODULE_LICENSE("GPL");
317MODULE_ALIAS("platform:dra7-atl");