aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisael Lopez Cruz2013-06-16 18:33:50 -0500
committerMisael Lopez Cruz2013-07-22 15:04:21 -0500
commit9dabab86b73716ba98e4a7cc4516e85a1d5e8507 (patch)
tree63871bc52d53691c4153cdf233b29a02d65d63db
parenta0d1af7abccce43ef3f1a5d3d585344367ecc89d (diff)
downloadkernel-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.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 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
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 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
134config 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
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 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
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");