diff options
author | Shawn Guo <shawn.guo@linaro.org> | 2012-03-16 04:56:40 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-04-01 06:28:27 -0400 |
commit | 60aae8da298e3ac0af07c8cdb6a98e47e8deab35 (patch) | |
tree | 3d8b42210aaff89459c8a7ec7185a8c7e4ac616f /sound/soc/fsl | |
parent | f19493a3d25ddf3ac7f27d846d54e95fb91af119 (diff) |
ASoC: fsl: create fsl_utils to accommodate the common functions
There is some amount of code duplication between mpc8610_hpcd and
p1022_ds machine drivers, and the same code will be duplicated again
when another new machine driver is added. The patch creates fsl_utils
to accommodate the common functions to stop the code duplication.
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_utils.c | 135 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_utils.h | 27 | ||||
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 157 | ||||
-rw-r--r-- | sound/soc/fsl/p1022_ds.c | 149 |
6 files changed, 189 insertions, 286 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index e3f75093d197..535ee73ecf15 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -1,6 +1,9 @@ | |||
1 | config SND_SOC_FSL_SSI | 1 | config SND_SOC_FSL_SSI |
2 | tristate | 2 | tristate |
3 | 3 | ||
4 | config SND_SOC_FSL_UTILS | ||
5 | tristate | ||
6 | |||
4 | menuconfig SND_POWERPC_SOC | 7 | menuconfig SND_POWERPC_SOC |
5 | tristate "SoC Audio for Freescale PowerPC CPUs" | 8 | tristate "SoC Audio for Freescale PowerPC CPUs" |
6 | depends on FSL_SOC | 9 | depends on FSL_SOC |
@@ -26,6 +29,7 @@ config SND_SOC_MPC8610_HPCD | |||
26 | # I2C is necessary for the CS4270 driver | 29 | # I2C is necessary for the CS4270 driver |
27 | depends on MPC8610_HPCD && I2C | 30 | depends on MPC8610_HPCD && I2C |
28 | select SND_SOC_FSL_SSI | 31 | select SND_SOC_FSL_SSI |
32 | select SND_SOC_FSL_UTILS | ||
29 | select SND_SOC_POWERPC_DMA | 33 | select SND_SOC_POWERPC_DMA |
30 | select SND_SOC_CS4270 | 34 | select SND_SOC_CS4270 |
31 | select SND_SOC_CS4270_VD33_ERRATA | 35 | select SND_SOC_CS4270_VD33_ERRATA |
@@ -38,6 +42,7 @@ config SND_SOC_P1022_DS | |||
38 | # I2C is necessary for the WM8776 driver | 42 | # I2C is necessary for the WM8776 driver |
39 | depends on P1022_DS && I2C | 43 | depends on P1022_DS && I2C |
40 | select SND_SOC_FSL_SSI | 44 | select SND_SOC_FSL_SSI |
45 | select SND_SOC_FSL_UTILS | ||
41 | select SND_SOC_POWERPC_DMA | 46 | select SND_SOC_POWERPC_DMA |
42 | select SND_SOC_WM8776 | 47 | select SND_SOC_WM8776 |
43 | default y if P1022_DS | 48 | default y if P1022_DS |
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index f031409a3929..fbdbed0af909 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -8,8 +8,10 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o | |||
8 | 8 | ||
9 | # Freescale PowerPC SSI/DMA Platform Support | 9 | # Freescale PowerPC SSI/DMA Platform Support |
10 | snd-soc-fsl-ssi-objs := fsl_ssi.o | 10 | snd-soc-fsl-ssi-objs := fsl_ssi.o |
11 | snd-soc-fsl-utils-objs := fsl_utils.o | ||
11 | snd-soc-fsl-dma-objs := fsl_dma.o | 12 | snd-soc-fsl-dma-objs := fsl_dma.o |
12 | obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o | 13 | obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o |
14 | obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o | ||
13 | obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o | 15 | obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o |
14 | 16 | ||
15 | # MPC5200 Platform Support | 17 | # MPC5200 Platform Support |
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c new file mode 100644 index 000000000000..4370c28e5362 --- /dev/null +++ b/sound/soc/fsl/fsl_utils.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /** | ||
2 | * Freescale ALSA SoC Machine driver utility | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2010 Freescale Semiconductor, Inc. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/of_address.h> | ||
15 | #include <linux/of_i2c.h> | ||
16 | #include <sound/soc.h> | ||
17 | |||
18 | #include "fsl_utils.h" | ||
19 | |||
20 | /** | ||
21 | * fsl_asoc_get_codec_dev_name - determine the dev_name for a codec node | ||
22 | * | ||
23 | * @np: pointer to the I2C device tree node | ||
24 | * @buf: buffer to be filled with the dev_name of the I2C device | ||
25 | * @len: the length of the buffer | ||
26 | * | ||
27 | * This function determines the dev_name for an I2C node. This is the name | ||
28 | * that would be returned by dev_name() if this device_node were part of a | ||
29 | * 'struct device' It's ugly and hackish, but it works. | ||
30 | * | ||
31 | * The dev_name for such devices include the bus number and I2C address. For | ||
32 | * example, "cs4270.0-004f". | ||
33 | */ | ||
34 | int fsl_asoc_get_codec_dev_name(struct device_node *np, char *buf, size_t len) | ||
35 | { | ||
36 | const u32 *iprop; | ||
37 | u32 addr; | ||
38 | char temp[DAI_NAME_SIZE]; | ||
39 | struct i2c_client *i2c; | ||
40 | |||
41 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
42 | |||
43 | iprop = of_get_property(np, "reg", NULL); | ||
44 | if (!iprop) | ||
45 | return -EINVAL; | ||
46 | |||
47 | addr = be32_to_cpup(iprop); | ||
48 | |||
49 | /* We need the adapter number */ | ||
50 | i2c = of_find_i2c_device_by_node(np); | ||
51 | if (!i2c) { | ||
52 | put_device(&i2c->dev); | ||
53 | return -ENODEV; | ||
54 | } | ||
55 | |||
56 | snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr); | ||
57 | put_device(&i2c->dev); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | EXPORT_SYMBOL(fsl_asoc_get_codec_dev_name); | ||
62 | |||
63 | /** | ||
64 | * fsl_asoc_get_dma_channel - determine the dma channel for a SSI node | ||
65 | * | ||
66 | * @ssi_np: pointer to the SSI device tree node | ||
67 | * @name: name of the phandle pointing to the dma channel | ||
68 | * @dai: ASoC DAI link pointer to be filled with platform_name | ||
69 | * @dma_channel_id: dma channel id to be returned | ||
70 | * @dma_id: dma id to be returned | ||
71 | * | ||
72 | * This function determines the dma and channel id for given SSI node. It | ||
73 | * also discovers the platform_name for the ASoC DAI link. | ||
74 | */ | ||
75 | int fsl_asoc_get_dma_channel(struct device_node *ssi_np, | ||
76 | const char *name, | ||
77 | struct snd_soc_dai_link *dai, | ||
78 | unsigned int *dma_channel_id, | ||
79 | unsigned int *dma_id) | ||
80 | { | ||
81 | struct resource res; | ||
82 | struct device_node *dma_channel_np, *dma_np; | ||
83 | const u32 *iprop; | ||
84 | int ret; | ||
85 | |||
86 | dma_channel_np = of_parse_phandle(ssi_np, name, 0); | ||
87 | if (!dma_channel_np) | ||
88 | return -EINVAL; | ||
89 | |||
90 | if (!of_device_is_compatible(dma_channel_np, "fsl,ssi-dma-channel")) { | ||
91 | of_node_put(dma_channel_np); | ||
92 | return -EINVAL; | ||
93 | } | ||
94 | |||
95 | /* Determine the dev_name for the device_node. This code mimics the | ||
96 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
97 | * the dev_name() of the device to match the platform (DMA) device with | ||
98 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
99 | * now). | ||
100 | * | ||
101 | * dai->platform name should already point to an allocated buffer. | ||
102 | */ | ||
103 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
104 | if (ret) { | ||
105 | of_node_put(dma_channel_np); | ||
106 | return ret; | ||
107 | } | ||
108 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
109 | (unsigned long long) res.start, dma_channel_np->name); | ||
110 | |||
111 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
112 | if (!iprop) { | ||
113 | of_node_put(dma_channel_np); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | *dma_channel_id = be32_to_cpup(iprop); | ||
117 | |||
118 | dma_np = of_get_parent(dma_channel_np); | ||
119 | iprop = of_get_property(dma_np, "cell-index", NULL); | ||
120 | if (!iprop) { | ||
121 | of_node_put(dma_np); | ||
122 | return -EINVAL; | ||
123 | } | ||
124 | *dma_id = be32_to_cpup(iprop); | ||
125 | |||
126 | of_node_put(dma_np); | ||
127 | of_node_put(dma_channel_np); | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | EXPORT_SYMBOL(fsl_asoc_get_dma_channel); | ||
132 | |||
133 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | ||
134 | MODULE_DESCRIPTION("Freescale ASoC utility code"); | ||
135 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h new file mode 100644 index 000000000000..44d143670c37 --- /dev/null +++ b/sound/soc/fsl/fsl_utils.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /** | ||
2 | * Freescale ALSA SoC Machine driver utility | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2010 Freescale Semiconductor, Inc. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #ifndef _FSL_UTILS_H | ||
14 | #define _FSL_UTILS_H | ||
15 | |||
16 | #define DAI_NAME_SIZE 32 | ||
17 | |||
18 | struct snd_soc_dai_link; | ||
19 | struct device_node; | ||
20 | |||
21 | int fsl_asoc_get_codec_dev_name(struct device_node *np, char *buf, size_t len); | ||
22 | int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name, | ||
23 | struct snd_soc_dai_link *dai, | ||
24 | unsigned int *dma_channel_id, | ||
25 | unsigned int *dma_id); | ||
26 | |||
27 | #endif /* _FSL_UTILS_H */ | ||
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index afbabf427f27..41951820b50e 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -14,18 +14,16 @@ | |||
14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
15 | #include <linux/of_device.h> | 15 | #include <linux/of_device.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/of_i2c.h> | ||
18 | #include <sound/soc.h> | 17 | #include <sound/soc.h> |
19 | #include <asm/fsl_guts.h> | 18 | #include <asm/fsl_guts.h> |
20 | 19 | ||
21 | #include "fsl_dma.h" | 20 | #include "fsl_dma.h" |
22 | #include "fsl_ssi.h" | 21 | #include "fsl_ssi.h" |
22 | #include "fsl_utils.h" | ||
23 | 23 | ||
24 | /* There's only one global utilities register */ | 24 | /* There's only one global utilities register */ |
25 | static phys_addr_t guts_phys; | 25 | static phys_addr_t guts_phys; |
26 | 26 | ||
27 | #define DAI_NAME_SIZE 32 | ||
28 | |||
29 | /** | 27 | /** |
30 | * mpc8610_hpcd_data: machine-specific ASoC device data | 28 | * mpc8610_hpcd_data: machine-specific ASoC device data |
31 | * | 29 | * |
@@ -181,141 +179,6 @@ static struct snd_soc_ops mpc8610_hpcd_ops = { | |||
181 | }; | 179 | }; |
182 | 180 | ||
183 | /** | 181 | /** |
184 | * get_node_by_phandle_name - get a node by its phandle name | ||
185 | * | ||
186 | * This function takes a node, the name of a property in that node, and a | ||
187 | * compatible string. Assuming the property is a phandle to another node, | ||
188 | * it returns that node, (optionally) if that node is compatible. | ||
189 | * | ||
190 | * If the property is not a phandle, or the node it points to is not compatible | ||
191 | * with the specific string, then NULL is returned. | ||
192 | */ | ||
193 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
194 | const char *name, | ||
195 | const char *compatible) | ||
196 | { | ||
197 | const phandle *ph; | ||
198 | int len; | ||
199 | |||
200 | ph = of_get_property(np, name, &len); | ||
201 | if (!ph || (len != sizeof(phandle))) | ||
202 | return NULL; | ||
203 | |||
204 | np = of_find_node_by_phandle(*ph); | ||
205 | if (!np) | ||
206 | return NULL; | ||
207 | |||
208 | if (compatible && !of_device_is_compatible(np, compatible)) { | ||
209 | of_node_put(np); | ||
210 | return NULL; | ||
211 | } | ||
212 | |||
213 | return np; | ||
214 | } | ||
215 | |||
216 | /** | ||
217 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
218 | * | ||
219 | * Return the value of the cell-index property of the parent of the given | ||
220 | * node. This is used for DMA channel nodes that need to know the DMA ID | ||
221 | * of the controller they are on. | ||
222 | */ | ||
223 | static int get_parent_cell_index(struct device_node *np) | ||
224 | { | ||
225 | struct device_node *parent = of_get_parent(np); | ||
226 | const u32 *iprop; | ||
227 | |||
228 | if (!parent) | ||
229 | return -1; | ||
230 | |||
231 | iprop = of_get_property(parent, "cell-index", NULL); | ||
232 | of_node_put(parent); | ||
233 | |||
234 | if (!iprop) | ||
235 | return -1; | ||
236 | |||
237 | return be32_to_cpup(iprop); | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * codec_node_dev_name - determine the dev_name for a codec node | ||
242 | * | ||
243 | * This function determines the dev_name for an I2C node. This is the name | ||
244 | * that would be returned by dev_name() if this device_node were part of a | ||
245 | * 'struct device' It's ugly and hackish, but it works. | ||
246 | * | ||
247 | * The dev_name for such devices include the bus number and I2C address. For | ||
248 | * example, "cs4270.0-004f". | ||
249 | */ | ||
250 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) | ||
251 | { | ||
252 | const u32 *iprop; | ||
253 | int addr; | ||
254 | char temp[DAI_NAME_SIZE]; | ||
255 | struct i2c_client *i2c; | ||
256 | |||
257 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
258 | |||
259 | iprop = of_get_property(np, "reg", NULL); | ||
260 | if (!iprop) | ||
261 | return -EINVAL; | ||
262 | |||
263 | addr = be32_to_cpup(iprop); | ||
264 | |||
265 | /* We need the adapter number */ | ||
266 | i2c = of_find_i2c_device_by_node(np); | ||
267 | if (!i2c) | ||
268 | return -ENODEV; | ||
269 | |||
270 | snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int get_dma_channel(struct device_node *ssi_np, | ||
276 | const char *name, | ||
277 | struct snd_soc_dai_link *dai, | ||
278 | unsigned int *dma_channel_id, | ||
279 | unsigned int *dma_id) | ||
280 | { | ||
281 | struct resource res; | ||
282 | struct device_node *dma_channel_np; | ||
283 | const u32 *iprop; | ||
284 | int ret; | ||
285 | |||
286 | dma_channel_np = get_node_by_phandle_name(ssi_np, name, | ||
287 | "fsl,ssi-dma-channel"); | ||
288 | if (!dma_channel_np) | ||
289 | return -EINVAL; | ||
290 | |||
291 | /* Determine the dev_name for the device_node. This code mimics the | ||
292 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
293 | * the dev_name() of the device to match the platform (DMA) device with | ||
294 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
295 | * now). | ||
296 | * | ||
297 | * dai->platform name should already point to an allocated buffer. | ||
298 | */ | ||
299 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
300 | if (ret) | ||
301 | return ret; | ||
302 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
303 | (unsigned long long) res.start, dma_channel_np->name); | ||
304 | |||
305 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
306 | if (!iprop) { | ||
307 | of_node_put(dma_channel_np); | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | |||
311 | *dma_channel_id = be32_to_cpup(iprop); | ||
312 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
313 | of_node_put(dma_channel_np); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | /** | ||
319 | * mpc8610_hpcd_probe: platform probe function for the machine driver | 182 | * mpc8610_hpcd_probe: platform probe function for the machine driver |
320 | * | 183 | * |
321 | * Although this is a machine driver, the SSI node is the "master" node with | 184 | * Although this is a machine driver, the SSI node is the "master" node with |
@@ -353,8 +216,8 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) | |||
353 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; | 216 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; |
354 | 217 | ||
355 | /* Determine the codec name, it will be used as the codec DAI name */ | 218 | /* Determine the codec name, it will be used as the codec DAI name */ |
356 | ret = codec_node_dev_name(codec_np, machine_data->codec_name, | 219 | ret = fsl_asoc_get_codec_dev_name(codec_np, machine_data->codec_name, |
357 | DAI_NAME_SIZE); | 220 | DAI_NAME_SIZE); |
358 | if (ret) { | 221 | if (ret) { |
359 | dev_err(&pdev->dev, "invalid codec node %s\n", | 222 | dev_err(&pdev->dev, "invalid codec node %s\n", |
360 | codec_np->full_name); | 223 | codec_np->full_name); |
@@ -458,9 +321,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) | |||
458 | 321 | ||
459 | /* Find the playback DMA channel to use. */ | 322 | /* Find the playback DMA channel to use. */ |
460 | machine_data->dai[0].platform_name = machine_data->platform_name[0]; | 323 | machine_data->dai[0].platform_name = machine_data->platform_name[0]; |
461 | ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], | 324 | ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", |
462 | &machine_data->dma_channel_id[0], | 325 | &machine_data->dai[0], |
463 | &machine_data->dma_id[0]); | 326 | &machine_data->dma_channel_id[0], |
327 | &machine_data->dma_id[0]); | ||
464 | if (ret) { | 328 | if (ret) { |
465 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); | 329 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); |
466 | goto error; | 330 | goto error; |
@@ -468,9 +332,10 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) | |||
468 | 332 | ||
469 | /* Find the capture DMA channel to use. */ | 333 | /* Find the capture DMA channel to use. */ |
470 | machine_data->dai[1].platform_name = machine_data->platform_name[1]; | 334 | machine_data->dai[1].platform_name = machine_data->platform_name[1]; |
471 | ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], | 335 | ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", |
472 | &machine_data->dma_channel_id[1], | 336 | &machine_data->dai[1], |
473 | &machine_data->dma_id[1]); | 337 | &machine_data->dma_channel_id[1], |
338 | &machine_data->dma_id[1]); | ||
474 | if (ret) { | 339 | if (ret) { |
475 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); | 340 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); |
476 | goto error; | 341 | goto error; |
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 46623405a2ce..be79e7388c0f 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c | |||
@@ -14,12 +14,12 @@ | |||
14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
15 | #include <linux/of_device.h> | 15 | #include <linux/of_device.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/of_i2c.h> | ||
18 | #include <sound/soc.h> | 17 | #include <sound/soc.h> |
19 | #include <asm/fsl_guts.h> | 18 | #include <asm/fsl_guts.h> |
20 | 19 | ||
21 | #include "fsl_dma.h" | 20 | #include "fsl_dma.h" |
22 | #include "fsl_ssi.h" | 21 | #include "fsl_ssi.h" |
22 | #include "fsl_utils.h" | ||
23 | 23 | ||
24 | /* P1022-specific PMUXCR and DMUXCR bit definitions */ | 24 | /* P1022-specific PMUXCR and DMUXCR bit definitions */ |
25 | 25 | ||
@@ -57,8 +57,6 @@ static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem *guts, | |||
57 | /* There's only one global utilities register */ | 57 | /* There's only one global utilities register */ |
58 | static phys_addr_t guts_phys; | 58 | static phys_addr_t guts_phys; |
59 | 59 | ||
60 | #define DAI_NAME_SIZE 32 | ||
61 | |||
62 | /** | 60 | /** |
63 | * machine_data: machine-specific ASoC device data | 61 | * machine_data: machine-specific ASoC device data |
64 | * | 62 | * |
@@ -191,136 +189,6 @@ static struct snd_soc_ops p1022_ds_ops = { | |||
191 | }; | 189 | }; |
192 | 190 | ||
193 | /** | 191 | /** |
194 | * get_node_by_phandle_name - get a node by its phandle name | ||
195 | * | ||
196 | * This function takes a node, the name of a property in that node, and a | ||
197 | * compatible string. Assuming the property is a phandle to another node, | ||
198 | * it returns that node, (optionally) if that node is compatible. | ||
199 | * | ||
200 | * If the property is not a phandle, or the node it points to is not compatible | ||
201 | * with the specific string, then NULL is returned. | ||
202 | */ | ||
203 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
204 | const char *name, const char *compatible) | ||
205 | { | ||
206 | np = of_parse_phandle(np, name, 0); | ||
207 | if (!np) | ||
208 | return NULL; | ||
209 | |||
210 | if (!of_device_is_compatible(np, compatible)) { | ||
211 | of_node_put(np); | ||
212 | return NULL; | ||
213 | } | ||
214 | |||
215 | return np; | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
220 | * | ||
221 | * Return the value of the cell-index property of the parent of the given | ||
222 | * node. This is used for DMA channel nodes that need to know the DMA ID | ||
223 | * of the controller they are on. | ||
224 | */ | ||
225 | static int get_parent_cell_index(struct device_node *np) | ||
226 | { | ||
227 | struct device_node *parent = of_get_parent(np); | ||
228 | const u32 *iprop; | ||
229 | int ret = -1; | ||
230 | |||
231 | if (!parent) | ||
232 | return -1; | ||
233 | |||
234 | iprop = of_get_property(parent, "cell-index", NULL); | ||
235 | if (iprop) | ||
236 | ret = be32_to_cpup(iprop); | ||
237 | |||
238 | of_node_put(parent); | ||
239 | |||
240 | return ret; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * codec_node_dev_name - determine the dev_name for a codec node | ||
245 | * | ||
246 | * This function determines the dev_name for an I2C node. This is the name | ||
247 | * that would be returned by dev_name() if this device_node were part of a | ||
248 | * 'struct device' It's ugly and hackish, but it works. | ||
249 | * | ||
250 | * The dev_name for such devices include the bus number and I2C address. For | ||
251 | * example, "cs4270-codec.0-004f". | ||
252 | */ | ||
253 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) | ||
254 | { | ||
255 | const u32 *iprop; | ||
256 | int addr; | ||
257 | char temp[DAI_NAME_SIZE]; | ||
258 | struct i2c_client *i2c; | ||
259 | |||
260 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
261 | |||
262 | iprop = of_get_property(np, "reg", NULL); | ||
263 | if (!iprop) | ||
264 | return -EINVAL; | ||
265 | |||
266 | addr = be32_to_cpup(iprop); | ||
267 | |||
268 | /* We need the adapter number */ | ||
269 | i2c = of_find_i2c_device_by_node(np); | ||
270 | if (!i2c) | ||
271 | return -ENODEV; | ||
272 | |||
273 | snprintf(buf, len, "%s.%u-%04x", temp, i2c->adapter->nr, addr); | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static int get_dma_channel(struct device_node *ssi_np, | ||
279 | const char *name, | ||
280 | struct snd_soc_dai_link *dai, | ||
281 | unsigned int *dma_channel_id, | ||
282 | unsigned int *dma_id) | ||
283 | { | ||
284 | struct resource res; | ||
285 | struct device_node *dma_channel_np; | ||
286 | const u32 *iprop; | ||
287 | int ret; | ||
288 | |||
289 | dma_channel_np = get_node_by_phandle_name(ssi_np, name, | ||
290 | "fsl,ssi-dma-channel"); | ||
291 | if (!dma_channel_np) | ||
292 | return -EINVAL; | ||
293 | |||
294 | /* Determine the dev_name for the device_node. This code mimics the | ||
295 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
296 | * the dev_name() of the device to match the platform (DMA) device with | ||
297 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
298 | * now). | ||
299 | * | ||
300 | * dai->platform name should already point to an allocated buffer. | ||
301 | */ | ||
302 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
303 | if (ret) { | ||
304 | of_node_put(dma_channel_np); | ||
305 | return ret; | ||
306 | } | ||
307 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
308 | (unsigned long long) res.start, dma_channel_np->name); | ||
309 | |||
310 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
311 | if (!iprop) { | ||
312 | of_node_put(dma_channel_np); | ||
313 | return -EINVAL; | ||
314 | } | ||
315 | |||
316 | *dma_channel_id = be32_to_cpup(iprop); | ||
317 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
318 | of_node_put(dma_channel_np); | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * p1022_ds_probe: platform probe function for the machine driver | 192 | * p1022_ds_probe: platform probe function for the machine driver |
325 | * | 193 | * |
326 | * Although this is a machine driver, the SSI node is the "master" node with | 194 | * Although this is a machine driver, the SSI node is the "master" node with |
@@ -358,7 +226,8 @@ static int p1022_ds_probe(struct platform_device *pdev) | |||
358 | mdata->dai[0].ops = &p1022_ds_ops; | 226 | mdata->dai[0].ops = &p1022_ds_ops; |
359 | 227 | ||
360 | /* Determine the codec name, it will be used as the codec DAI name */ | 228 | /* Determine the codec name, it will be used as the codec DAI name */ |
361 | ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE); | 229 | ret = fsl_asoc_get_codec_dev_name(codec_np, mdata->codec_name, |
230 | DAI_NAME_SIZE); | ||
362 | if (ret) { | 231 | if (ret) { |
363 | dev_err(&pdev->dev, "invalid codec node %s\n", | 232 | dev_err(&pdev->dev, "invalid codec node %s\n", |
364 | codec_np->full_name); | 233 | codec_np->full_name); |
@@ -462,9 +331,9 @@ static int p1022_ds_probe(struct platform_device *pdev) | |||
462 | 331 | ||
463 | /* Find the playback DMA channel to use. */ | 332 | /* Find the playback DMA channel to use. */ |
464 | mdata->dai[0].platform_name = mdata->platform_name[0]; | 333 | mdata->dai[0].platform_name = mdata->platform_name[0]; |
465 | ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], | 334 | ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], |
466 | &mdata->dma_channel_id[0], | 335 | &mdata->dma_channel_id[0], |
467 | &mdata->dma_id[0]); | 336 | &mdata->dma_id[0]); |
468 | if (ret) { | 337 | if (ret) { |
469 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); | 338 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); |
470 | goto error; | 339 | goto error; |
@@ -472,9 +341,9 @@ static int p1022_ds_probe(struct platform_device *pdev) | |||
472 | 341 | ||
473 | /* Find the capture DMA channel to use. */ | 342 | /* Find the capture DMA channel to use. */ |
474 | mdata->dai[1].platform_name = mdata->platform_name[1]; | 343 | mdata->dai[1].platform_name = mdata->platform_name[1]; |
475 | ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], | 344 | ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], |
476 | &mdata->dma_channel_id[1], | 345 | &mdata->dma_channel_id[1], |
477 | &mdata->dma_id[1]); | 346 | &mdata->dma_id[1]); |
478 | if (ret) { | 347 | if (ret) { |
479 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); | 348 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); |
480 | goto error; | 349 | goto error; |