diff options
author | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-03-17 16:15:21 -0400 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-08-12 09:00:00 -0400 |
commit | f0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch) | |
tree | f6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/fsl | |
parent | bda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff) |
ASoC: multi-component - ASoC Multi-Component Support
This patch extends the ASoC API to allow sound cards to have more than one
CODEC and more than one platform DMA controller. This is achieved by dividing
some current ASoC structures that contain both driver data and device data into
structures that only either contain device data or driver data. i.e.
struct snd_soc_codec ---> struct snd_soc_codec (device data)
+-> struct snd_soc_codec_driver (driver data)
struct snd_soc_platform ---> struct snd_soc_platform (device data)
+-> struct snd_soc_platform_driver (driver data)
struct snd_soc_dai ---> struct snd_soc_dai (device data)
+-> struct snd_soc_dai_driver (driver data)
struct snd_soc_device ---> deleted
This now allows ASoC to be more tightly aligned with the Linux driver model and
also means that every ASoC codec, platform and (platform) DAI is a kernel
device. ASoC component private data is now stored as device private data.
The ASoC sound card struct snd_soc_card has also been updated to store lists
of it's components rather than a pointer to a codec and platform. The PCM
runtime struct soc_pcm_runtime now has pointers to all its components.
This patch adds DAPM support for ASoC multi-component and removes struct
snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec
or runtime PCM level basis rather than using snd_soc_socdev.
Other notable multi-component changes:-
* Stream operations now de-reference less structures.
* close_delayed work() now runs on a DAI basis rather than looping all DAIs
in a card.
* PM suspend()/resume() operations can now handle N CODECs and Platforms
per sound card.
* Added soc_bind_dai_link() to bind the component devices to the sound card.
* Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove
DAI link components.
* sysfs entries can now be registered per component per card.
* snd_soc_new_pcms() functionailty rolled into dai_link_probe().
* snd_soc_register_codec() now does all the codec list and mutex init.
This patch changes the probe() and remove() of the CODEC drivers as follows:-
o Make CODEC driver a platform driver
o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core.
o Removed all static codec pointers (drivers now support > 1 codec dev)
o snd_soc_register_pcms() now done by core.
o snd_soc_register_dai() folded into snd_soc_register_codec().
CS4270 portions:
Acked-by: Timur Tabi <timur@freescale.com>
Some TLV320aic23 and Cirrus platform fixes.
Signed-off-by: Ryan Mallon <ryan@bluewatersys.com>
TI CODEC and OMAP fixes
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Signed-off-by: Jarkko Nikula <jhnikula@gmail.com>
Samsung platform and misc fixes :-
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Jassi Brar <jassi.brar@samsung.com>
Signed-off-by: Seungwhan Youn <sw.youn@samsung.com>
MPC8610 and PPC fixes.
Signed-off-by: Timur Tabi <timur@freescale.com>
i.MX fixes and some core fixes.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
J4740 platform fixes:-
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
CC: Tony Lindgren <tony@atomide.com>
CC: Nicolas Ferre <nicolas.ferre@atmel.com>
CC: Kevin Hilman <khilman@deeprootsystems.com>
CC: Sascha Hauer <s.hauer@pengutronix.de>
CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
CC: Kuninori Morimoto <morimoto.kuninori@renesas.com>
CC: Daniel Gloeckner <dg@emlix.com>
CC: Manuel Lauss <mano@roarinelk.homelinux.net>
CC: Mike Frysinger <vapier.adi@gmail.com>
CC: Arnaud Patard <apatard@mandriva.com>
CC: Wan ZongShun <mcuos.com@gmail.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 3 | ||||
-rw-r--r-- | sound/soc/fsl/efika-audio-fabric.c | 20 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_dma.c | 312 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_dma.h | 20 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 249 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.h | 26 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_dma.c | 66 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_dma.h | 5 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_ac97.c | 34 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_ac97.h | 2 | ||||
-rw-r--r-- | sound/soc/fsl/mpc5200_psc_i2s.c | 19 | ||||
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 658 | ||||
-rw-r--r-- | sound/soc/fsl/pcm030-audio-fabric.c | 21 | ||||
-rw-r--r-- | sound/soc/fsl/soc-of-simple.c | 172 |
15 files changed, 728 insertions, 882 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8cb65ccad35f..981868700388 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -1,6 +1,3 @@ | |||
1 | config SND_SOC_OF_SIMPLE | ||
2 | tristate | ||
3 | |||
4 | config SND_MPC52xx_DMA | 1 | config SND_MPC52xx_DMA |
5 | tristate | 2 | tristate |
6 | 3 | ||
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index a83a73967ec6..7e472a53fcd3 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -1,6 +1,3 @@ | |||
1 | # Simple machine driver that extracts configuration from the OF device tree | ||
2 | obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o | ||
3 | |||
4 | # MPC8610 HPCD Machine Support | 1 | # MPC8610 HPCD Machine Support |
5 | snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o | 2 | snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o |
6 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o | 3 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o |
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c index 1a5b8e0d6a34..53251e6b5bd5 100644 --- a/sound/soc/fsl/efika-audio-fabric.c +++ b/sound/soc/fsl/efika-audio-fabric.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
25 | #include <sound/initval.h> | 25 | #include <sound/initval.h> |
26 | #include <sound/soc.h> | 26 | #include <sound/soc.h> |
27 | #include <sound/soc-of-simple.h> | ||
28 | 27 | ||
29 | #include "mpc5200_dma.h" | 28 | #include "mpc5200_dma.h" |
30 | #include "mpc5200_psc_ac97.h" | 29 | #include "mpc5200_psc_ac97.h" |
@@ -32,21 +31,24 @@ | |||
32 | 31 | ||
33 | #define DRV_NAME "efika-audio-fabric" | 32 | #define DRV_NAME "efika-audio-fabric" |
34 | 33 | ||
35 | static struct snd_soc_device device; | ||
36 | static struct snd_soc_card card; | 34 | static struct snd_soc_card card; |
37 | 35 | ||
38 | static struct snd_soc_dai_link efika_fabric_dai[] = { | 36 | static struct snd_soc_dai_link efika_fabric_dai[] = { |
39 | { | 37 | { |
40 | .name = "AC97", | 38 | .name = "AC97", |
41 | .stream_name = "AC97 Analog", | 39 | .stream_name = "AC97 Analog", |
42 | .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_ANALOG], | 40 | .codec_dai_name = "stac9766-hifi-analog", |
43 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL], | 41 | .cpu_dai_name = "mpc5200-psc-ac97.0", |
42 | .platform_name = "mpc5200-pcm-audio", | ||
43 | .codec_name = "stac9766-codec", | ||
44 | }, | 44 | }, |
45 | { | 45 | { |
46 | .name = "AC97", | 46 | .name = "AC97", |
47 | .stream_name = "AC97 IEC958", | 47 | .stream_name = "AC97 IEC958", |
48 | .codec_dai = &stac9766_dai[STAC9766_DAI_AC97_DIGITAL], | 48 | .codec_dai_name = "stac9766-hifi-IEC958", |
49 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF], | 49 | .cpu_dai_name = "mpc5200-psc-ac97.1", |
50 | .platform_name = "mpc5200-pcm-audio", | ||
51 | .codec_name = "stac9766-codec", | ||
50 | }, | 52 | }, |
51 | }; | 53 | }; |
52 | 54 | ||
@@ -58,13 +60,10 @@ static __init int efika_fabric_init(void) | |||
58 | if (!of_machine_is_compatible("bplan,efika")) | 60 | if (!of_machine_is_compatible("bplan,efika")) |
59 | return -ENODEV; | 61 | return -ENODEV; |
60 | 62 | ||
61 | card.platform = &mpc5200_audio_dma_platform; | ||
62 | card.name = "Efika"; | 63 | card.name = "Efika"; |
63 | card.dai_link = efika_fabric_dai; | 64 | card.dai_link = efika_fabric_dai; |
64 | card.num_links = ARRAY_SIZE(efika_fabric_dai); | 65 | card.num_links = ARRAY_SIZE(efika_fabric_dai); |
65 | 66 | ||
66 | device.card = &card; | ||
67 | device.codec_dev = &soc_codec_dev_stac9766; | ||
68 | 67 | ||
69 | pdev = platform_device_alloc("soc-audio", 1); | 68 | pdev = platform_device_alloc("soc-audio", 1); |
70 | if (!pdev) { | 69 | if (!pdev) { |
@@ -72,8 +71,7 @@ static __init int efika_fabric_init(void) | |||
72 | return -ENODEV; | 71 | return -ENODEV; |
73 | } | 72 | } |
74 | 73 | ||
75 | platform_set_drvdata(pdev, &device); | 74 | platform_set_drvdata(pdev, &card); |
76 | device.dev = &pdev->dev; | ||
77 | 75 | ||
78 | rc = platform_device_add(pdev); | 76 | rc = platform_device_add(pdev); |
79 | if (rc) { | 77 | if (rc) { |
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 410c7496a18d..d09e1941b1fa 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c | |||
@@ -3,10 +3,11 @@ | |||
3 | * | 3 | * |
4 | * Author: Timur Tabi <timur@freescale.com> | 4 | * Author: Timur Tabi <timur@freescale.com> |
5 | * | 5 | * |
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | 6 | * Copyright 2007-2010 Freescale Semiconductor, Inc. |
7 | * under the terms of the GNU General Public License version 2. This | 7 | * |
8 | * program is licensed "as is" without any warranty of any kind, whether | 8 | * This file is licensed under the terms of the GNU General Public License |
9 | * express or implied. | 9 | * version 2. This program is licensed "as is" without any warranty of any |
10 | * kind, whether express or implied. | ||
10 | * | 11 | * |
11 | * This driver implements ASoC support for the Elo DMA controller, which is | 12 | * This driver implements ASoC support for the Elo DMA controller, which is |
12 | * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms, | 13 | * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms, |
@@ -20,6 +21,8 @@ | |||
20 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
21 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
22 | #include <linux/gfp.h> | 23 | #include <linux/gfp.h> |
24 | #include <linux/of_platform.h> | ||
25 | #include <linux/list.h> | ||
23 | 26 | ||
24 | #include <sound/core.h> | 27 | #include <sound/core.h> |
25 | #include <sound/pcm.h> | 28 | #include <sound/pcm.h> |
@@ -29,6 +32,7 @@ | |||
29 | #include <asm/io.h> | 32 | #include <asm/io.h> |
30 | 33 | ||
31 | #include "fsl_dma.h" | 34 | #include "fsl_dma.h" |
35 | #include "fsl_ssi.h" /* For the offset of stx0 and srx0 */ | ||
32 | 36 | ||
33 | /* | 37 | /* |
34 | * The formats that the DMA controller supports, which is anything | 38 | * The formats that the DMA controller supports, which is anything |
@@ -52,26 +56,16 @@ | |||
52 | #define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ | 56 | #define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ |
53 | SNDRV_PCM_RATE_CONTINUOUS) | 57 | SNDRV_PCM_RATE_CONTINUOUS) |
54 | 58 | ||
55 | /* DMA global data. This structure is used by fsl_dma_open() to determine | 59 | struct dma_object { |
56 | * which DMA channels to assign to a substream. Unfortunately, ASoC V1 does | 60 | struct list_head list; |
57 | * not allow the machine driver to provide this information to the PCM | 61 | struct snd_soc_platform_driver dai; |
58 | * driver in advance, and there's no way to differentiate between the two | ||
59 | * DMA controllers. So for now, this driver only supports one SSI device | ||
60 | * using two DMA channels. We cannot support multiple DMA devices. | ||
61 | * | ||
62 | * ssi_stx_phys: bus address of SSI STX register | ||
63 | * ssi_srx_phys: bus address of SSI SRX register | ||
64 | * dma_channel: pointer to the DMA channel's registers | ||
65 | * irq: IRQ for this DMA channel | ||
66 | * assigned: set to 1 if that DMA channel is assigned to a substream | ||
67 | */ | ||
68 | static struct { | ||
69 | dma_addr_t ssi_stx_phys; | 62 | dma_addr_t ssi_stx_phys; |
70 | dma_addr_t ssi_srx_phys; | 63 | dma_addr_t ssi_srx_phys; |
71 | struct ccsr_dma_channel __iomem *dma_channel[2]; | 64 | struct ccsr_dma_channel __iomem *channel; |
72 | unsigned int irq[2]; | 65 | unsigned int irq; |
73 | unsigned int assigned[2]; | 66 | bool assigned; |
74 | } dma_global_data; | 67 | char path[1]; |
68 | }; | ||
75 | 69 | ||
76 | /* | 70 | /* |
77 | * The number of DMA links to use. Two is the bare minimum, but if you | 71 | * The number of DMA links to use. Two is the bare minimum, but if you |
@@ -88,8 +82,6 @@ static struct { | |||
88 | * structure. | 82 | * structure. |
89 | * | 83 | * |
90 | * @link[]: array of link descriptors | 84 | * @link[]: array of link descriptors |
91 | * @controller_id: which DMA controller (0, 1, ...) | ||
92 | * @channel_id: which DMA channel on the controller (0, 1, 2, ...) | ||
93 | * @dma_channel: pointer to the DMA channel's registers | 85 | * @dma_channel: pointer to the DMA channel's registers |
94 | * @irq: IRQ for this DMA channel | 86 | * @irq: IRQ for this DMA channel |
95 | * @substream: pointer to the substream object, needed by the ISR | 87 | * @substream: pointer to the substream object, needed by the ISR |
@@ -104,8 +96,6 @@ static struct { | |||
104 | */ | 96 | */ |
105 | struct fsl_dma_private { | 97 | struct fsl_dma_private { |
106 | struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; | 98 | struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; |
107 | unsigned int controller_id; | ||
108 | unsigned int channel_id; | ||
109 | struct ccsr_dma_channel __iomem *dma_channel; | 99 | struct ccsr_dma_channel __iomem *dma_channel; |
110 | unsigned int irq; | 100 | unsigned int irq; |
111 | struct snd_pcm_substream *substream; | 101 | struct snd_pcm_substream *substream; |
@@ -212,6 +202,9 @@ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) | |||
212 | static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | 202 | static irqreturn_t fsl_dma_isr(int irq, void *dev_id) |
213 | { | 203 | { |
214 | struct fsl_dma_private *dma_private = dev_id; | 204 | struct fsl_dma_private *dma_private = dev_id; |
205 | struct snd_pcm_substream *substream = dma_private->substream; | ||
206 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
207 | struct device *dev = rtd->platform->dev; | ||
215 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | 208 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; |
216 | irqreturn_t ret = IRQ_NONE; | 209 | irqreturn_t ret = IRQ_NONE; |
217 | u32 sr, sr2 = 0; | 210 | u32 sr, sr2 = 0; |
@@ -222,11 +215,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
222 | sr = in_be32(&dma_channel->sr); | 215 | sr = in_be32(&dma_channel->sr); |
223 | 216 | ||
224 | if (sr & CCSR_DMA_SR_TE) { | 217 | if (sr & CCSR_DMA_SR_TE) { |
225 | dev_err(dma_private->substream->pcm->card->dev, | 218 | dev_err(dev, "dma transmit error\n"); |
226 | "DMA transmit error (controller=%u channel=%u irq=%u\n", | 219 | fsl_dma_abort_stream(substream); |
227 | dma_private->controller_id, | ||
228 | dma_private->channel_id, irq); | ||
229 | fsl_dma_abort_stream(dma_private->substream); | ||
230 | sr2 |= CCSR_DMA_SR_TE; | 220 | sr2 |= CCSR_DMA_SR_TE; |
231 | ret = IRQ_HANDLED; | 221 | ret = IRQ_HANDLED; |
232 | } | 222 | } |
@@ -235,11 +225,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
235 | ret = IRQ_HANDLED; | 225 | ret = IRQ_HANDLED; |
236 | 226 | ||
237 | if (sr & CCSR_DMA_SR_PE) { | 227 | if (sr & CCSR_DMA_SR_PE) { |
238 | dev_err(dma_private->substream->pcm->card->dev, | 228 | dev_err(dev, "dma programming error\n"); |
239 | "DMA%u programming error (channel=%u irq=%u)\n", | 229 | fsl_dma_abort_stream(substream); |
240 | dma_private->controller_id, | ||
241 | dma_private->channel_id, irq); | ||
242 | fsl_dma_abort_stream(dma_private->substream); | ||
243 | sr2 |= CCSR_DMA_SR_PE; | 230 | sr2 |= CCSR_DMA_SR_PE; |
244 | ret = IRQ_HANDLED; | 231 | ret = IRQ_HANDLED; |
245 | } | 232 | } |
@@ -253,8 +240,6 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
253 | ret = IRQ_HANDLED; | 240 | ret = IRQ_HANDLED; |
254 | 241 | ||
255 | if (sr & CCSR_DMA_SR_EOSI) { | 242 | if (sr & CCSR_DMA_SR_EOSI) { |
256 | struct snd_pcm_substream *substream = dma_private->substream; | ||
257 | |||
258 | /* Tell ALSA we completed a period. */ | 243 | /* Tell ALSA we completed a period. */ |
259 | snd_pcm_period_elapsed(substream); | 244 | snd_pcm_period_elapsed(substream); |
260 | 245 | ||
@@ -305,10 +290,8 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
305 | fsl_dma_hardware.buffer_bytes_max, | 290 | fsl_dma_hardware.buffer_bytes_max, |
306 | &pcm->streams[0].substream->dma_buffer); | 291 | &pcm->streams[0].substream->dma_buffer); |
307 | if (ret) { | 292 | if (ret) { |
308 | dev_err(card->dev, | 293 | dev_err(card->dev, "can't allocate playback dma buffer\n"); |
309 | "Can't allocate playback DMA buffer (size=%u)\n", | 294 | return ret; |
310 | fsl_dma_hardware.buffer_bytes_max); | ||
311 | return -ENOMEM; | ||
312 | } | 295 | } |
313 | 296 | ||
314 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, | 297 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, |
@@ -316,10 +299,8 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
316 | &pcm->streams[1].substream->dma_buffer); | 299 | &pcm->streams[1].substream->dma_buffer); |
317 | if (ret) { | 300 | if (ret) { |
318 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | 301 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); |
319 | dev_err(card->dev, | 302 | dev_err(card->dev, "can't allocate capture dma buffer\n"); |
320 | "Can't allocate capture DMA buffer (size=%u)\n", | 303 | return ret; |
321 | fsl_dma_hardware.buffer_bytes_max); | ||
322 | return -ENOMEM; | ||
323 | } | 304 | } |
324 | 305 | ||
325 | return 0; | 306 | return 0; |
@@ -390,6 +371,10 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
390 | static int fsl_dma_open(struct snd_pcm_substream *substream) | 371 | static int fsl_dma_open(struct snd_pcm_substream *substream) |
391 | { | 372 | { |
392 | struct snd_pcm_runtime *runtime = substream->runtime; | 373 | struct snd_pcm_runtime *runtime = substream->runtime; |
374 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
375 | struct device *dev = rtd->platform->dev; | ||
376 | struct dma_object *dma = | ||
377 | container_of(rtd->platform->driver, struct dma_object, dai); | ||
393 | struct fsl_dma_private *dma_private; | 378 | struct fsl_dma_private *dma_private; |
394 | struct ccsr_dma_channel __iomem *dma_channel; | 379 | struct ccsr_dma_channel __iomem *dma_channel; |
395 | dma_addr_t ld_buf_phys; | 380 | dma_addr_t ld_buf_phys; |
@@ -407,52 +392,44 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) | |||
407 | ret = snd_pcm_hw_constraint_integer(runtime, | 392 | ret = snd_pcm_hw_constraint_integer(runtime, |
408 | SNDRV_PCM_HW_PARAM_PERIODS); | 393 | SNDRV_PCM_HW_PARAM_PERIODS); |
409 | if (ret < 0) { | 394 | if (ret < 0) { |
410 | dev_err(substream->pcm->card->dev, "invalid buffer size\n"); | 395 | dev_err(dev, "invalid buffer size\n"); |
411 | return ret; | 396 | return ret; |
412 | } | 397 | } |
413 | 398 | ||
414 | channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | 399 | channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; |
415 | 400 | ||
416 | if (dma_global_data.assigned[channel]) { | 401 | if (dma->assigned) { |
417 | dev_err(substream->pcm->card->dev, | 402 | dev_err(dev, "dma channel already assigned\n"); |
418 | "DMA channel already assigned\n"); | ||
419 | return -EBUSY; | 403 | return -EBUSY; |
420 | } | 404 | } |
421 | 405 | ||
422 | dma_private = dma_alloc_coherent(substream->pcm->card->dev, | 406 | dma_private = dma_alloc_coherent(dev, sizeof(struct fsl_dma_private), |
423 | sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL); | 407 | &ld_buf_phys, GFP_KERNEL); |
424 | if (!dma_private) { | 408 | if (!dma_private) { |
425 | dev_err(substream->pcm->card->dev, | 409 | dev_err(dev, "can't allocate dma private data\n"); |
426 | "can't allocate DMA private data\n"); | ||
427 | return -ENOMEM; | 410 | return -ENOMEM; |
428 | } | 411 | } |
429 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 412 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
430 | dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys; | 413 | dma_private->ssi_sxx_phys = dma->ssi_stx_phys; |
431 | else | 414 | else |
432 | dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys; | 415 | dma_private->ssi_sxx_phys = dma->ssi_srx_phys; |
433 | 416 | ||
434 | dma_private->dma_channel = dma_global_data.dma_channel[channel]; | 417 | dma_private->dma_channel = dma->channel; |
435 | dma_private->irq = dma_global_data.irq[channel]; | 418 | dma_private->irq = dma->irq; |
436 | dma_private->substream = substream; | 419 | dma_private->substream = substream; |
437 | dma_private->ld_buf_phys = ld_buf_phys; | 420 | dma_private->ld_buf_phys = ld_buf_phys; |
438 | dma_private->dma_buf_phys = substream->dma_buffer.addr; | 421 | dma_private->dma_buf_phys = substream->dma_buffer.addr; |
439 | 422 | ||
440 | /* We only support one DMA controller for now */ | ||
441 | dma_private->controller_id = 0; | ||
442 | dma_private->channel_id = channel; | ||
443 | |||
444 | ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private); | 423 | ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private); |
445 | if (ret) { | 424 | if (ret) { |
446 | dev_err(substream->pcm->card->dev, | 425 | dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", |
447 | "can't register ISR for IRQ %u (ret=%i)\n", | ||
448 | dma_private->irq, ret); | 426 | dma_private->irq, ret); |
449 | dma_free_coherent(substream->pcm->card->dev, | 427 | dma_free_coherent(dev, sizeof(struct fsl_dma_private), |
450 | sizeof(struct fsl_dma_private), | ||
451 | dma_private, dma_private->ld_buf_phys); | 428 | dma_private, dma_private->ld_buf_phys); |
452 | return ret; | 429 | return ret; |
453 | } | 430 | } |
454 | 431 | ||
455 | dma_global_data.assigned[channel] = 1; | 432 | dma->assigned = 1; |
456 | 433 | ||
457 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 434 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
458 | snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); | 435 | snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); |
@@ -546,6 +523,8 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
546 | { | 523 | { |
547 | struct snd_pcm_runtime *runtime = substream->runtime; | 524 | struct snd_pcm_runtime *runtime = substream->runtime; |
548 | struct fsl_dma_private *dma_private = runtime->private_data; | 525 | struct fsl_dma_private *dma_private = runtime->private_data; |
526 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
527 | struct device *dev = rtd->platform->dev; | ||
549 | 528 | ||
550 | /* Number of bits per sample */ | 529 | /* Number of bits per sample */ |
551 | unsigned int sample_size = | 530 | unsigned int sample_size = |
@@ -606,8 +585,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
606 | break; | 585 | break; |
607 | default: | 586 | default: |
608 | /* We should never get here */ | 587 | /* We should never get here */ |
609 | dev_err(substream->pcm->card->dev, | 588 | dev_err(dev, "unsupported sample size %u\n", sample_size); |
610 | "unsupported sample size %u\n", sample_size); | ||
611 | return -EINVAL; | 589 | return -EINVAL; |
612 | } | 590 | } |
613 | 591 | ||
@@ -689,6 +667,8 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) | |||
689 | { | 667 | { |
690 | struct snd_pcm_runtime *runtime = substream->runtime; | 668 | struct snd_pcm_runtime *runtime = substream->runtime; |
691 | struct fsl_dma_private *dma_private = runtime->private_data; | 669 | struct fsl_dma_private *dma_private = runtime->private_data; |
670 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
671 | struct device *dev = rtd->platform->dev; | ||
692 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | 672 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; |
693 | dma_addr_t position; | 673 | dma_addr_t position; |
694 | snd_pcm_uframes_t frames; | 674 | snd_pcm_uframes_t frames; |
@@ -710,8 +690,7 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) | |||
710 | 690 | ||
711 | if ((position < dma_private->dma_buf_phys) || | 691 | if ((position < dma_private->dma_buf_phys) || |
712 | (position > dma_private->dma_buf_end)) { | 692 | (position > dma_private->dma_buf_end)) { |
713 | dev_err(substream->pcm->card->dev, | 693 | dev_err(dev, "dma pointer is out of range, halting stream\n"); |
714 | "dma pointer is out of range, halting stream\n"); | ||
715 | return SNDRV_PCM_POS_XRUN; | 694 | return SNDRV_PCM_POS_XRUN; |
716 | } | 695 | } |
717 | 696 | ||
@@ -772,26 +751,28 @@ static int fsl_dma_close(struct snd_pcm_substream *substream) | |||
772 | { | 751 | { |
773 | struct snd_pcm_runtime *runtime = substream->runtime; | 752 | struct snd_pcm_runtime *runtime = substream->runtime; |
774 | struct fsl_dma_private *dma_private = runtime->private_data; | 753 | struct fsl_dma_private *dma_private = runtime->private_data; |
775 | int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | 754 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
755 | struct device *dev = rtd->platform->dev; | ||
756 | struct dma_object *dma = | ||
757 | container_of(rtd->platform->driver, struct dma_object, dai); | ||
776 | 758 | ||
777 | if (dma_private) { | 759 | if (dma_private) { |
778 | if (dma_private->irq) | 760 | if (dma_private->irq) |
779 | free_irq(dma_private->irq, dma_private); | 761 | free_irq(dma_private->irq, dma_private); |
780 | 762 | ||
781 | if (dma_private->ld_buf_phys) { | 763 | if (dma_private->ld_buf_phys) { |
782 | dma_unmap_single(substream->pcm->card->dev, | 764 | dma_unmap_single(dev, dma_private->ld_buf_phys, |
783 | dma_private->ld_buf_phys, | 765 | sizeof(dma_private->link), |
784 | sizeof(dma_private->link), DMA_TO_DEVICE); | 766 | DMA_TO_DEVICE); |
785 | } | 767 | } |
786 | 768 | ||
787 | /* Deallocate the fsl_dma_private structure */ | 769 | /* Deallocate the fsl_dma_private structure */ |
788 | dma_free_coherent(substream->pcm->card->dev, | 770 | dma_free_coherent(dev, sizeof(struct fsl_dma_private), |
789 | sizeof(struct fsl_dma_private), | 771 | dma_private, dma_private->ld_buf_phys); |
790 | dma_private, dma_private->ld_buf_phys); | ||
791 | substream->runtime->private_data = NULL; | 772 | substream->runtime->private_data = NULL; |
792 | } | 773 | } |
793 | 774 | ||
794 | dma_global_data.assigned[dir] = 0; | 775 | dma->assigned = 0; |
795 | 776 | ||
796 | return 0; | 777 | return 0; |
797 | } | 778 | } |
@@ -814,6 +795,40 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) | |||
814 | } | 795 | } |
815 | } | 796 | } |
816 | 797 | ||
798 | /* List of DMA nodes that we've probed */ | ||
799 | static LIST_HEAD(dma_list); | ||
800 | |||
801 | /** | ||
802 | * find_ssi_node -- returns the SSI node that points to his DMA channel node | ||
803 | * | ||
804 | * Although this DMA driver attempts to operate independently of the other | ||
805 | * devices, it still needs to determine some information about the SSI device | ||
806 | * that it's working with. Unfortunately, the device tree does not contain | ||
807 | * a pointer from the DMA channel node to the SSI node -- the pointer goes the | ||
808 | * other way. So we need to scan the device tree for SSI nodes until we find | ||
809 | * the one that points to the given DMA channel node. It's ugly, but at least | ||
810 | * it's contained in this one function. | ||
811 | */ | ||
812 | static struct device_node *find_ssi_node(struct device_node *dma_channel_np) | ||
813 | { | ||
814 | struct device_node *ssi_np, *np; | ||
815 | |||
816 | for_each_compatible_node(ssi_np, NULL, "fsl,mpc8610-ssi") { | ||
817 | /* Check each DMA phandle to see if it points to us. We | ||
818 | * assume that device_node pointers are a valid comparison. | ||
819 | */ | ||
820 | np = of_parse_phandle(ssi_np, "fsl,playback-dma", 0); | ||
821 | if (np == dma_channel_np) | ||
822 | return ssi_np; | ||
823 | |||
824 | np = of_parse_phandle(ssi_np, "fsl,capture-dma", 0); | ||
825 | if (np == dma_channel_np) | ||
826 | return ssi_np; | ||
827 | } | ||
828 | |||
829 | return NULL; | ||
830 | } | ||
831 | |||
817 | static struct snd_pcm_ops fsl_dma_ops = { | 832 | static struct snd_pcm_ops fsl_dma_ops = { |
818 | .open = fsl_dma_open, | 833 | .open = fsl_dma_open, |
819 | .close = fsl_dma_close, | 834 | .close = fsl_dma_close, |
@@ -823,59 +838,112 @@ static struct snd_pcm_ops fsl_dma_ops = { | |||
823 | .pointer = fsl_dma_pointer, | 838 | .pointer = fsl_dma_pointer, |
824 | }; | 839 | }; |
825 | 840 | ||
826 | struct snd_soc_platform fsl_soc_platform = { | 841 | static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, |
827 | .name = "fsl-dma", | 842 | const struct of_device_id *match) |
828 | .pcm_ops = &fsl_dma_ops, | 843 | { |
829 | .pcm_new = fsl_dma_new, | 844 | struct dma_object *dma; |
830 | .pcm_free = fsl_dma_free_dma_buffers, | 845 | struct device_node *np = of_dev->dev.of_node; |
831 | }; | 846 | struct device_node *ssi_np; |
832 | EXPORT_SYMBOL_GPL(fsl_soc_platform); | 847 | struct resource res; |
848 | int ret; | ||
833 | 849 | ||
834 | /** | 850 | /* Find the SSI node that points to us. */ |
835 | * fsl_dma_configure: store the DMA parameters from the fabric driver. | 851 | ssi_np = find_ssi_node(np); |
836 | * | 852 | if (!ssi_np) { |
837 | * This function is called by the ASoC fabric driver to give us the DMA and | 853 | dev_err(&of_dev->dev, "cannot find parent SSI node\n"); |
838 | * SSI channel information. | 854 | return -ENODEV; |
839 | * | 855 | } |
840 | * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI | 856 | |
841 | * data when a substream is created, so for now we need to store this data | 857 | ret = of_address_to_resource(ssi_np, 0, &res); |
842 | * into a global variable. This means that we can only support one DMA | 858 | of_node_put(ssi_np); |
843 | * controller, and hence only one SSI. | 859 | if (ret) { |
844 | */ | 860 | dev_err(&of_dev->dev, "could not determine device resources\n"); |
845 | int fsl_dma_configure(struct fsl_dma_info *dma_info) | 861 | return ret; |
862 | } | ||
863 | |||
864 | dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); | ||
865 | if (!dma) { | ||
866 | dev_err(&of_dev->dev, "could not allocate dma object\n"); | ||
867 | return -ENOMEM; | ||
868 | } | ||
869 | |||
870 | strcpy(dma->path, np->full_name); | ||
871 | dma->dai.ops = &fsl_dma_ops; | ||
872 | dma->dai.pcm_new = fsl_dma_new; | ||
873 | dma->dai.pcm_free = fsl_dma_free_dma_buffers; | ||
874 | |||
875 | /* Store the SSI-specific information that we need */ | ||
876 | dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); | ||
877 | dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); | ||
878 | |||
879 | ret = snd_soc_register_platform(&of_dev->dev, &dma->dai); | ||
880 | if (ret) { | ||
881 | dev_err(&of_dev->dev, "could not register platform\n"); | ||
882 | kfree(dma); | ||
883 | return ret; | ||
884 | } | ||
885 | |||
886 | dma->channel = of_iomap(np, 0); | ||
887 | dma->irq = irq_of_parse_and_map(np, 0); | ||
888 | list_add(&dma->list, &dma_list); | ||
889 | |||
890 | return 0; | ||
891 | } | ||
892 | |||
893 | static int __devexit fsl_soc_dma_remove(struct of_device *of_dev) | ||
846 | { | 894 | { |
847 | static int initialized; | 895 | struct list_head *n, *ptr; |
896 | struct dma_object *dma; | ||
848 | 897 | ||
849 | /* We only support one DMA controller for now */ | 898 | list_for_each_safe(ptr, n, &dma_list) { |
850 | if (initialized) | 899 | dma = list_entry(ptr, struct dma_object, list); |
851 | return 0; | 900 | list_del_init(ptr); |
901 | |||
902 | snd_soc_unregister_platform(&of_dev->dev); | ||
903 | iounmap(dma->channel); | ||
904 | irq_dispose_mapping(dma->irq); | ||
905 | kfree(dma); | ||
906 | } | ||
852 | 907 | ||
853 | dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys; | 908 | return 0; |
854 | dma_global_data.ssi_srx_phys = dma_info->ssi_srx_phys; | ||
855 | dma_global_data.dma_channel[0] = dma_info->dma_channel[0]; | ||
856 | dma_global_data.dma_channel[1] = dma_info->dma_channel[1]; | ||
857 | dma_global_data.irq[0] = dma_info->dma_irq[0]; | ||
858 | dma_global_data.irq[1] = dma_info->dma_irq[1]; | ||
859 | dma_global_data.assigned[0] = 0; | ||
860 | dma_global_data.assigned[1] = 0; | ||
861 | |||
862 | initialized = 1; | ||
863 | return 1; | ||
864 | } | 909 | } |
865 | EXPORT_SYMBOL_GPL(fsl_dma_configure); | ||
866 | 910 | ||
867 | static int __init fsl_soc_platform_init(void) | 911 | static const struct of_device_id fsl_soc_dma_ids[] = { |
912 | { .compatible = "fsl,ssi-dma-channel", }, | ||
913 | {} | ||
914 | }; | ||
915 | MODULE_DEVICE_TABLE(of, fsl_soc_dma_ids); | ||
916 | |||
917 | static struct of_platform_driver fsl_soc_dma_driver = { | ||
918 | .driver = { | ||
919 | .name = "fsl-pcm-audio", | ||
920 | .owner = THIS_MODULE, | ||
921 | .of_match_table = fsl_soc_dma_ids, | ||
922 | }, | ||
923 | .probe = fsl_soc_dma_probe, | ||
924 | .remove = __devexit_p(fsl_soc_dma_remove), | ||
925 | }; | ||
926 | |||
927 | static int __init fsl_soc_dma_init(void) | ||
868 | { | 928 | { |
869 | return snd_soc_register_platform(&fsl_soc_platform); | 929 | pr_info("Freescale Elo DMA ASoC PCM Driver\n"); |
930 | |||
931 | return of_register_platform_driver(&fsl_soc_dma_driver); | ||
870 | } | 932 | } |
871 | module_init(fsl_soc_platform_init); | ||
872 | 933 | ||
873 | static void __exit fsl_soc_platform_exit(void) | 934 | static void __exit fsl_soc_dma_exit(void) |
874 | { | 935 | { |
875 | snd_soc_unregister_platform(&fsl_soc_platform); | 936 | of_unregister_platform_driver(&fsl_soc_dma_driver); |
876 | } | 937 | } |
877 | module_exit(fsl_soc_platform_exit); | 938 | |
939 | /* We want the DMA driver to be initialized before the SSI driver, so that | ||
940 | * when the SSI driver calls fsl_soc_dma_dai_from_node(), the DMA driver | ||
941 | * will already have been probed. The easiest way to do that is to make the | ||
942 | * __init function called via arch_initcall(). | ||
943 | */ | ||
944 | module_init(fsl_soc_dma_init); | ||
945 | module_exit(fsl_soc_dma_exit); | ||
878 | 946 | ||
879 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 947 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
880 | MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module"); | 948 | MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM Driver"); |
881 | MODULE_LICENSE("GPL"); | 949 | MODULE_LICENSE("GPL v2"); |
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h index 385d4a42603c..78fee97e8036 100644 --- a/sound/soc/fsl/fsl_dma.h +++ b/sound/soc/fsl/fsl_dma.h | |||
@@ -126,24 +126,4 @@ struct fsl_dma_link_descriptor { | |||
126 | u8 res[4]; /* Reserved */ | 126 | u8 res[4]; /* Reserved */ |
127 | } __attribute__ ((aligned(32), packed)); | 127 | } __attribute__ ((aligned(32), packed)); |
128 | 128 | ||
129 | /* DMA information needed to create a snd_soc_dai object | ||
130 | * | ||
131 | * ssi_stx_phys: bus address of SSI STX register to use | ||
132 | * ssi_srx_phys: bus address of SSI SRX register to use | ||
133 | * dma[0]: points to the DMA channel to use for playback | ||
134 | * dma[1]: points to the DMA channel to use for capture | ||
135 | * dma_irq[0]: IRQ of the DMA channel to use for playback | ||
136 | * dma_irq[1]: IRQ of the DMA channel to use for capture | ||
137 | */ | ||
138 | struct fsl_dma_info { | ||
139 | dma_addr_t ssi_stx_phys; | ||
140 | dma_addr_t ssi_srx_phys; | ||
141 | struct ccsr_dma_channel __iomem *dma_channel[2]; | ||
142 | unsigned int dma_irq[2]; | ||
143 | }; | ||
144 | |||
145 | extern struct snd_soc_platform fsl_soc_platform; | ||
146 | |||
147 | int fsl_dma_configure(struct fsl_dma_info *dma_info); | ||
148 | |||
149 | #endif | 129 | #endif |
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 762c1b8e8e4e..64f65910a7d7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -3,10 +3,11 @@ | |||
3 | * | 3 | * |
4 | * Author: Timur Tabi <timur@freescale.com> | 4 | * Author: Timur Tabi <timur@freescale.com> |
5 | * | 5 | * |
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | 6 | * Copyright 2007-2010 Freescale Semiconductor, Inc. |
7 | * under the terms of the GNU General Public License version 2. This | 7 | * |
8 | * program is licensed "as is" without any warranty of any kind, whether | 8 | * This file is licensed under the terms of the GNU General Public License |
9 | * express or implied. | 9 | * version 2. This program is licensed "as is" without any warranty of any |
10 | * kind, whether express or implied. | ||
10 | */ | 11 | */ |
11 | 12 | ||
12 | #include <linux/init.h> | 13 | #include <linux/init.h> |
@@ -15,6 +16,7 @@ | |||
15 | #include <linux/device.h> | 16 | #include <linux/device.h> |
16 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
17 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/of_platform.h> | ||
18 | 20 | ||
19 | #include <sound/core.h> | 21 | #include <sound/core.h> |
20 | #include <sound/pcm.h> | 22 | #include <sound/pcm.h> |
@@ -71,33 +73,31 @@ | |||
71 | /** | 73 | /** |
72 | * fsl_ssi_private: per-SSI private data | 74 | * fsl_ssi_private: per-SSI private data |
73 | * | 75 | * |
74 | * @name: short name for this device ("SSI0", "SSI1", etc) | ||
75 | * @ssi: pointer to the SSI's registers | 76 | * @ssi: pointer to the SSI's registers |
76 | * @ssi_phys: physical address of the SSI registers | 77 | * @ssi_phys: physical address of the SSI registers |
77 | * @irq: IRQ of this SSI | 78 | * @irq: IRQ of this SSI |
78 | * @first_stream: pointer to the stream that was opened first | 79 | * @first_stream: pointer to the stream that was opened first |
79 | * @second_stream: pointer to second stream | 80 | * @second_stream: pointer to second stream |
80 | * @dev: struct device pointer | ||
81 | * @playback: the number of playback streams opened | 81 | * @playback: the number of playback streams opened |
82 | * @capture: the number of capture streams opened | 82 | * @capture: the number of capture streams opened |
83 | * @asynchronous: 0=synchronous mode, 1=asynchronous mode | 83 | * @asynchronous: 0=synchronous mode, 1=asynchronous mode |
84 | * @cpu_dai: the CPU DAI for this device | 84 | * @cpu_dai: the CPU DAI for this device |
85 | * @dev_attr: the sysfs device attribute structure | 85 | * @dev_attr: the sysfs device attribute structure |
86 | * @stats: SSI statistics | 86 | * @stats: SSI statistics |
87 | * @name: name for this device | ||
87 | */ | 88 | */ |
88 | struct fsl_ssi_private { | 89 | struct fsl_ssi_private { |
89 | char name[8]; | ||
90 | struct ccsr_ssi __iomem *ssi; | 90 | struct ccsr_ssi __iomem *ssi; |
91 | dma_addr_t ssi_phys; | 91 | dma_addr_t ssi_phys; |
92 | unsigned int irq; | 92 | unsigned int irq; |
93 | struct snd_pcm_substream *first_stream; | 93 | struct snd_pcm_substream *first_stream; |
94 | struct snd_pcm_substream *second_stream; | 94 | struct snd_pcm_substream *second_stream; |
95 | struct device *dev; | ||
96 | unsigned int playback; | 95 | unsigned int playback; |
97 | unsigned int capture; | 96 | unsigned int capture; |
98 | int asynchronous; | 97 | int asynchronous; |
99 | struct snd_soc_dai cpu_dai; | 98 | struct snd_soc_dai_driver cpu_dai_drv; |
100 | struct device_attribute dev_attr; | 99 | struct device_attribute dev_attr; |
100 | struct platform_device *pdev; | ||
101 | 101 | ||
102 | struct { | 102 | struct { |
103 | unsigned int rfrc; | 103 | unsigned int rfrc; |
@@ -122,6 +122,8 @@ struct fsl_ssi_private { | |||
122 | unsigned int tfe1; | 122 | unsigned int tfe1; |
123 | unsigned int tfe0; | 123 | unsigned int tfe0; |
124 | } stats; | 124 | } stats; |
125 | |||
126 | char name[1]; | ||
125 | }; | 127 | }; |
126 | 128 | ||
127 | /** | 129 | /** |
@@ -280,7 +282,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
280 | struct snd_soc_dai *dai) | 282 | struct snd_soc_dai *dai) |
281 | { | 283 | { |
282 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 284 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
283 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | 285 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
284 | 286 | ||
285 | /* | 287 | /* |
286 | * If this is the first stream opened, then request the IRQ | 288 | * If this is the first stream opened, then request the IRQ |
@@ -290,6 +292,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
290 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 292 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
291 | int ret; | 293 | int ret; |
292 | 294 | ||
295 | /* The 'name' should not have any slashes in it. */ | ||
293 | ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, | 296 | ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, |
294 | ssi_private->name, ssi_private); | 297 | ssi_private->name, ssi_private); |
295 | if (ret < 0) { | 298 | if (ret < 0) { |
@@ -422,7 +425,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
422 | static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, | 425 | static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, |
423 | struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) | 426 | struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) |
424 | { | 427 | { |
425 | struct fsl_ssi_private *ssi_private = cpu_dai->private_data; | 428 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); |
426 | 429 | ||
427 | if (substream == ssi_private->first_stream) { | 430 | if (substream == ssi_private->first_stream) { |
428 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 431 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
@@ -458,7 +461,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | |||
458 | struct snd_soc_dai *dai) | 461 | struct snd_soc_dai *dai) |
459 | { | 462 | { |
460 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 463 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
461 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | 464 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
462 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 465 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
463 | 466 | ||
464 | switch (cmd) { | 467 | switch (cmd) { |
@@ -497,7 +500,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, | |||
497 | struct snd_soc_dai *dai) | 500 | struct snd_soc_dai *dai) |
498 | { | 501 | { |
499 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 502 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
500 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | 503 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
501 | 504 | ||
502 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 505 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
503 | ssi_private->playback--; | 506 | ssi_private->playback--; |
@@ -523,56 +526,15 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, | |||
523 | } | 526 | } |
524 | } | 527 | } |
525 | 528 | ||
526 | /** | ||
527 | * fsl_ssi_set_sysclk: set the clock frequency and direction | ||
528 | * | ||
529 | * This function is called by the machine driver to tell us what the clock | ||
530 | * frequency and direction are. | ||
531 | * | ||
532 | * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), | ||
533 | * and we don't care about the frequency. Return an error if the direction | ||
534 | * is not SND_SOC_CLOCK_IN. | ||
535 | * | ||
536 | * @clk_id: reserved, should be zero | ||
537 | * @freq: the frequency of the given clock ID, currently ignored | ||
538 | * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) | ||
539 | */ | ||
540 | static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai, | ||
541 | int clk_id, unsigned int freq, int dir) | ||
542 | { | ||
543 | |||
544 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; | ||
545 | } | ||
546 | |||
547 | /** | ||
548 | * fsl_ssi_set_fmt: set the serial format. | ||
549 | * | ||
550 | * This function is called by the machine driver to tell us what serial | ||
551 | * format to use. | ||
552 | * | ||
553 | * Currently, we only support I2S mode. Return an error if the format is | ||
554 | * not SND_SOC_DAIFMT_I2S. | ||
555 | * | ||
556 | * @format: one of SND_SOC_DAIFMT_xxx | ||
557 | */ | ||
558 | static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | ||
559 | { | ||
560 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; | ||
561 | } | ||
562 | |||
563 | /** | ||
564 | * fsl_ssi_dai_template: template CPU DAI for the SSI | ||
565 | */ | ||
566 | static struct snd_soc_dai_ops fsl_ssi_dai_ops = { | 529 | static struct snd_soc_dai_ops fsl_ssi_dai_ops = { |
567 | .startup = fsl_ssi_startup, | 530 | .startup = fsl_ssi_startup, |
568 | .hw_params = fsl_ssi_hw_params, | 531 | .hw_params = fsl_ssi_hw_params, |
569 | .shutdown = fsl_ssi_shutdown, | 532 | .shutdown = fsl_ssi_shutdown, |
570 | .trigger = fsl_ssi_trigger, | 533 | .trigger = fsl_ssi_trigger, |
571 | .set_sysclk = fsl_ssi_set_sysclk, | ||
572 | .set_fmt = fsl_ssi_set_fmt, | ||
573 | }; | 534 | }; |
574 | 535 | ||
575 | static struct snd_soc_dai fsl_ssi_dai_template = { | 536 | /* Template for the CPU dai driver structure */ |
537 | static struct snd_soc_dai_driver fsl_ssi_dai_template = { | ||
576 | .playback = { | 538 | .playback = { |
577 | /* The SSI does not support monaural audio. */ | 539 | /* The SSI does not support monaural audio. */ |
578 | .channels_min = 2, | 540 | .channels_min = 2, |
@@ -640,95 +602,176 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev, | |||
640 | } | 602 | } |
641 | 603 | ||
642 | /** | 604 | /** |
643 | * fsl_ssi_create_dai: create a snd_soc_dai structure | 605 | * Make every character in a string lower-case |
644 | * | ||
645 | * This function is called by the machine driver to create a snd_soc_dai | ||
646 | * structure. The function creates an ssi_private object, which contains | ||
647 | * the snd_soc_dai. It also creates the sysfs statistics device. | ||
648 | */ | 606 | */ |
649 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | 607 | static void make_lowercase(char *s) |
608 | { | ||
609 | char *p = s; | ||
610 | char c; | ||
611 | |||
612 | while ((c = *p)) { | ||
613 | if ((c >= 'A') && (c <= 'Z')) | ||
614 | *p = c + ('a' - 'A'); | ||
615 | p++; | ||
616 | } | ||
617 | } | ||
618 | |||
619 | static int __devinit fsl_ssi_probe(struct of_device *of_dev, | ||
620 | const struct of_device_id *match) | ||
650 | { | 621 | { |
651 | struct snd_soc_dai *fsl_ssi_dai; | ||
652 | struct fsl_ssi_private *ssi_private; | 622 | struct fsl_ssi_private *ssi_private; |
653 | int ret = 0; | 623 | int ret = 0; |
654 | struct device_attribute *dev_attr; | 624 | struct device_attribute *dev_attr; |
625 | struct device_node *np = of_dev->dev.of_node; | ||
626 | const char *p, *sprop; | ||
627 | struct resource res; | ||
628 | char name[64]; | ||
655 | 629 | ||
656 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); | 630 | /* We are only interested in SSIs with a codec phandle in them, so let's |
631 | * make sure this SSI has one. | ||
632 | */ | ||
633 | if (!of_get_property(np, "codec-handle", NULL)) | ||
634 | return -ENODEV; | ||
635 | |||
636 | /* We only support the SSI in "I2S Slave" mode */ | ||
637 | sprop = of_get_property(np, "fsl,mode", NULL); | ||
638 | if (!sprop || strcmp(sprop, "i2s-slave")) { | ||
639 | dev_notice(&of_dev->dev, "mode %s is unsupported\n", sprop); | ||
640 | return -ENODEV; | ||
641 | } | ||
642 | |||
643 | /* The DAI name is the last part of the full name of the node. */ | ||
644 | p = strrchr(np->full_name, '/') + 1; | ||
645 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), | ||
646 | GFP_KERNEL); | ||
657 | if (!ssi_private) { | 647 | if (!ssi_private) { |
658 | dev_err(ssi_info->dev, "could not allocate DAI object\n"); | 648 | dev_err(&of_dev->dev, "could not allocate DAI object\n"); |
659 | return NULL; | 649 | return -ENOMEM; |
660 | } | 650 | } |
661 | memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template, | ||
662 | sizeof(struct snd_soc_dai)); | ||
663 | 651 | ||
664 | fsl_ssi_dai = &ssi_private->cpu_dai; | 652 | strcpy(ssi_private->name, p); |
665 | dev_attr = &ssi_private->dev_attr; | ||
666 | 653 | ||
667 | sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); | 654 | /* Initialize this copy of the CPU DAI driver structure */ |
668 | ssi_private->ssi = ssi_info->ssi; | 655 | memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, |
669 | ssi_private->ssi_phys = ssi_info->ssi_phys; | 656 | sizeof(fsl_ssi_dai_template)); |
670 | ssi_private->irq = ssi_info->irq; | 657 | ssi_private->cpu_dai_drv.name = ssi_private->name; |
671 | ssi_private->dev = ssi_info->dev; | 658 | |
672 | ssi_private->asynchronous = ssi_info->asynchronous; | 659 | /* Get the addresses and IRQ */ |
660 | ret = of_address_to_resource(np, 0, &res); | ||
661 | if (ret) { | ||
662 | dev_err(&of_dev->dev, "could not determine device resources\n"); | ||
663 | kfree(ssi_private); | ||
664 | return ret; | ||
665 | } | ||
666 | ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start); | ||
667 | ssi_private->ssi_phys = res.start; | ||
668 | ssi_private->irq = irq_of_parse_and_map(np, 0); | ||
673 | 669 | ||
674 | dev_set_drvdata(ssi_private->dev, fsl_ssi_dai); | 670 | /* Are the RX and the TX clocks locked? */ |
671 | if (of_find_property(np, "fsl,ssi-asynchronous", NULL)) | ||
672 | ssi_private->asynchronous = 1; | ||
673 | else | ||
674 | ssi_private->cpu_dai_drv.symmetric_rates = 1; | ||
675 | 675 | ||
676 | /* Initialize the the device_attribute structure */ | 676 | /* Initialize the the device_attribute structure */ |
677 | dev_attr->attr.name = "ssi-stats"; | 677 | dev_attr = &ssi_private->dev_attr; |
678 | dev_attr->attr.name = "statistics"; | ||
678 | dev_attr->attr.mode = S_IRUGO; | 679 | dev_attr->attr.mode = S_IRUGO; |
679 | dev_attr->show = fsl_sysfs_ssi_show; | 680 | dev_attr->show = fsl_sysfs_ssi_show; |
680 | 681 | ||
681 | ret = device_create_file(ssi_private->dev, dev_attr); | 682 | ret = device_create_file(&of_dev->dev, dev_attr); |
682 | if (ret) { | 683 | if (ret) { |
683 | dev_err(ssi_info->dev, "could not create sysfs %s file\n", | 684 | dev_err(&of_dev->dev, "could not create sysfs %s file\n", |
684 | ssi_private->dev_attr.attr.name); | 685 | ssi_private->dev_attr.attr.name); |
685 | kfree(fsl_ssi_dai); | 686 | kfree(ssi_private); |
686 | return NULL; | 687 | return ret; |
687 | } | 688 | } |
688 | 689 | ||
689 | fsl_ssi_dai->private_data = ssi_private; | 690 | /* Register with ASoC */ |
690 | fsl_ssi_dai->name = ssi_private->name; | 691 | dev_set_drvdata(&of_dev->dev, ssi_private); |
691 | fsl_ssi_dai->id = ssi_info->id; | ||
692 | fsl_ssi_dai->dev = ssi_info->dev; | ||
693 | fsl_ssi_dai->symmetric_rates = 1; | ||
694 | 692 | ||
695 | ret = snd_soc_register_dai(fsl_ssi_dai); | 693 | ret = snd_soc_register_dai(&of_dev->dev, &ssi_private->cpu_dai_drv); |
696 | if (ret != 0) { | 694 | if (ret != 0) { |
697 | dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret); | 695 | dev_err(&of_dev->dev, "failed to register DAI: %d\n", ret); |
698 | kfree(fsl_ssi_dai); | 696 | kfree(ssi_private); |
699 | return NULL; | 697 | return ret; |
698 | } | ||
699 | |||
700 | /* Trigger the machine driver's probe function. The platform driver | ||
701 | * name of the machine driver is taken from the /model property of the | ||
702 | * device tree. We also pass the address of the CPU DAI driver | ||
703 | * structure. | ||
704 | */ | ||
705 | sprop = of_get_property(of_find_node_by_path("/"), "model", NULL); | ||
706 | /* Sometimes the model name has a "fsl," prefix, so we strip that. */ | ||
707 | p = strrchr(sprop, ','); | ||
708 | if (p) | ||
709 | sprop = p + 1; | ||
710 | snprintf(name, sizeof(name), "snd-soc-%s", sprop); | ||
711 | make_lowercase(name); | ||
712 | |||
713 | ssi_private->pdev = | ||
714 | platform_device_register_data(&of_dev->dev, name, 0, NULL, 0); | ||
715 | if (IS_ERR(ssi_private->pdev)) { | ||
716 | ret = PTR_ERR(ssi_private->pdev); | ||
717 | dev_err(&of_dev->dev, "failed to register platform: %d\n", ret); | ||
718 | kfree(ssi_private); | ||
719 | return ret; | ||
700 | } | 720 | } |
701 | 721 | ||
702 | return fsl_ssi_dai; | 722 | return 0; |
703 | } | 723 | } |
704 | EXPORT_SYMBOL_GPL(fsl_ssi_create_dai); | ||
705 | 724 | ||
706 | /** | 725 | /** |
707 | * fsl_ssi_destroy_dai: destroy the snd_soc_dai object | 726 | * fsl_ssi_destroy_dai: destroy the snd_soc_dai object |
708 | * | 727 | * |
709 | * This function undoes the operations of fsl_ssi_create_dai() | 728 | * This function undoes the operations of fsl_ssi_probe() |
710 | */ | 729 | */ |
711 | void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) | 730 | static int fsl_ssi_remove(struct of_device *of_dev) |
712 | { | 731 | { |
713 | struct fsl_ssi_private *ssi_private = | 732 | struct fsl_ssi_private *ssi_private = dev_get_drvdata(&of_dev->dev); |
714 | container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai); | ||
715 | 733 | ||
716 | device_remove_file(ssi_private->dev, &ssi_private->dev_attr); | 734 | platform_device_unregister(ssi_private->pdev); |
717 | 735 | snd_soc_unregister_dai(&of_dev->dev); | |
718 | snd_soc_unregister_dai(&ssi_private->cpu_dai); | 736 | device_remove_file(&of_dev->dev, &ssi_private->dev_attr); |
719 | 737 | ||
720 | kfree(ssi_private); | 738 | kfree(ssi_private); |
739 | dev_set_drvdata(&of_dev->dev, NULL); | ||
740 | |||
741 | return 0; | ||
721 | } | 742 | } |
722 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); | 743 | |
744 | static const struct of_device_id fsl_ssi_ids[] = { | ||
745 | { .compatible = "fsl,mpc8610-ssi", }, | ||
746 | {} | ||
747 | }; | ||
748 | MODULE_DEVICE_TABLE(of, fsl_ssi_ids); | ||
749 | |||
750 | static struct of_platform_driver fsl_ssi_driver = { | ||
751 | .driver = { | ||
752 | .name = "fsl-ssi-dai", | ||
753 | .owner = THIS_MODULE, | ||
754 | .of_match_table = fsl_ssi_ids, | ||
755 | }, | ||
756 | .probe = fsl_ssi_probe, | ||
757 | .remove = fsl_ssi_remove, | ||
758 | }; | ||
723 | 759 | ||
724 | static int __init fsl_ssi_init(void) | 760 | static int __init fsl_ssi_init(void) |
725 | { | 761 | { |
726 | printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); | 762 | printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); |
727 | 763 | ||
728 | return 0; | 764 | return of_register_platform_driver(&fsl_ssi_driver); |
765 | } | ||
766 | |||
767 | static void __exit fsl_ssi_exit(void) | ||
768 | { | ||
769 | of_unregister_platform_driver(&fsl_ssi_driver); | ||
729 | } | 770 | } |
771 | |||
730 | module_init(fsl_ssi_init); | 772 | module_init(fsl_ssi_init); |
773 | module_exit(fsl_ssi_exit); | ||
731 | 774 | ||
732 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 775 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
733 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | 776 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); |
734 | MODULE_LICENSE("GPL"); | 777 | MODULE_LICENSE("GPL v2"); |
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index eade01feaab6..217300029b5b 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h | |||
@@ -196,31 +196,5 @@ struct ccsr_ssi { | |||
196 | #define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) | 196 | #define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) |
197 | #define CCSR_SSI_SOR_SYNRST 0x00000001 | 197 | #define CCSR_SSI_SOR_SYNRST 0x00000001 |
198 | 198 | ||
199 | /* Instantiation data for an SSI interface | ||
200 | * | ||
201 | * This structure contains all the information that the the SSI driver needs | ||
202 | * to instantiate an SSI interface with ALSA. The machine driver should | ||
203 | * create this structure, fill it in, call fsl_ssi_create_dai(), and then | ||
204 | * delete the structure. | ||
205 | * | ||
206 | * id: which SSI this is (0, 1, etc. ) | ||
207 | * ssi: pointer to the SSI's registers | ||
208 | * ssi_phys: physical address of the SSI registers | ||
209 | * irq: IRQ of this SSI | ||
210 | * dev: struct device, used to create the sysfs statistics file | ||
211 | * asynchronous: 0=synchronous mode, 1=asynchronous mode | ||
212 | */ | ||
213 | struct fsl_ssi_info { | ||
214 | unsigned int id; | ||
215 | struct ccsr_ssi __iomem *ssi; | ||
216 | dma_addr_t ssi_phys; | ||
217 | unsigned int irq; | ||
218 | struct device *dev; | ||
219 | int asynchronous; | ||
220 | }; | ||
221 | |||
222 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); | ||
223 | void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai); | ||
224 | |||
225 | #endif | 199 | #endif |
226 | 200 | ||
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 1d4e7164e80a..dce6b551cd78 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c | |||
@@ -9,6 +9,8 @@ | |||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/of_device.h> | 10 | #include <linux/of_device.h> |
11 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
12 | #include <linux/of_device.h> | ||
13 | #include <linux/of_platform.h> | ||
12 | 14 | ||
13 | #include <sound/soc.h> | 15 | #include <sound/soc.h> |
14 | 16 | ||
@@ -107,7 +109,7 @@ static int psc_dma_hw_free(struct snd_pcm_substream *substream) | |||
107 | static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) | 109 | static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) |
108 | { | 110 | { |
109 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 111 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
110 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 112 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
111 | struct snd_pcm_runtime *runtime = substream->runtime; | 113 | struct snd_pcm_runtime *runtime = substream->runtime; |
112 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); | 114 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); |
113 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | 115 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; |
@@ -212,7 +214,7 @@ static int psc_dma_open(struct snd_pcm_substream *substream) | |||
212 | { | 214 | { |
213 | struct snd_pcm_runtime *runtime = substream->runtime; | 215 | struct snd_pcm_runtime *runtime = substream->runtime; |
214 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 216 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
215 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 217 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
216 | struct psc_dma_stream *s; | 218 | struct psc_dma_stream *s; |
217 | int rc; | 219 | int rc; |
218 | 220 | ||
@@ -239,7 +241,7 @@ static int psc_dma_open(struct snd_pcm_substream *substream) | |||
239 | static int psc_dma_close(struct snd_pcm_substream *substream) | 241 | static int psc_dma_close(struct snd_pcm_substream *substream) |
240 | { | 242 | { |
241 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 243 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
242 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 244 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
243 | struct psc_dma_stream *s; | 245 | struct psc_dma_stream *s; |
244 | 246 | ||
245 | dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); | 247 | dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); |
@@ -264,7 +266,7 @@ static snd_pcm_uframes_t | |||
264 | psc_dma_pointer(struct snd_pcm_substream *substream) | 266 | psc_dma_pointer(struct snd_pcm_substream *substream) |
265 | { | 267 | { |
266 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 268 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
267 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 269 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
268 | struct psc_dma_stream *s; | 270 | struct psc_dma_stream *s; |
269 | dma_addr_t count; | 271 | dma_addr_t count; |
270 | 272 | ||
@@ -302,11 +304,11 @@ static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
302 | struct snd_pcm *pcm) | 304 | struct snd_pcm *pcm) |
303 | { | 305 | { |
304 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; | 306 | struct snd_soc_pcm_runtime *rtd = pcm->private_data; |
305 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 307 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
306 | size_t size = psc_dma_hardware.buffer_bytes_max; | 308 | size_t size = psc_dma_hardware.buffer_bytes_max; |
307 | int rc = 0; | 309 | int rc = 0; |
308 | 310 | ||
309 | dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", | 311 | dev_dbg(rtd->platform->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", |
310 | card, dai, pcm); | 312 | card, dai, pcm); |
311 | 313 | ||
312 | if (!card->dev->dma_mask) | 314 | if (!card->dev->dma_mask) |
@@ -328,8 +330,8 @@ static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
328 | goto capture_alloc_err; | 330 | goto capture_alloc_err; |
329 | } | 331 | } |
330 | 332 | ||
331 | if (rtd->socdev->card->codec->ac97) | 333 | if (rtd->codec->ac97) |
332 | rtd->socdev->card->codec->ac97->private_data = psc_dma; | 334 | rtd->codec->ac97->private_data = psc_dma; |
333 | 335 | ||
334 | return 0; | 336 | return 0; |
335 | 337 | ||
@@ -349,7 +351,7 @@ static void psc_dma_free(struct snd_pcm *pcm) | |||
349 | struct snd_pcm_substream *substream; | 351 | struct snd_pcm_substream *substream; |
350 | int stream; | 352 | int stream; |
351 | 353 | ||
352 | dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm); | 354 | dev_dbg(rtd->platform->dev, "psc_dma_free(pcm=%p)\n", pcm); |
353 | 355 | ||
354 | for (stream = 0; stream < 2; stream++) { | 356 | for (stream = 0; stream < 2; stream++) { |
355 | substream = pcm->streams[stream].substream; | 357 | substream = pcm->streams[stream].substream; |
@@ -361,15 +363,14 @@ static void psc_dma_free(struct snd_pcm *pcm) | |||
361 | } | 363 | } |
362 | } | 364 | } |
363 | 365 | ||
364 | struct snd_soc_platform mpc5200_audio_dma_platform = { | 366 | static struct snd_soc_platform_driver mpc5200_audio_dma_platform = { |
365 | .name = "mpc5200-psc-audio", | 367 | .ops = &psc_dma_ops, |
366 | .pcm_ops = &psc_dma_ops, | ||
367 | .pcm_new = &psc_dma_new, | 368 | .pcm_new = &psc_dma_new, |
368 | .pcm_free = &psc_dma_free, | 369 | .pcm_free = &psc_dma_free, |
369 | }; | 370 | }; |
370 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform); | ||
371 | 371 | ||
372 | int mpc5200_audio_dma_create(struct of_device *op) | 372 | static int mpc5200_hpcd_probe(struct of_device *op, |
373 | const struct of_device_id *match) | ||
373 | { | 374 | { |
374 | phys_addr_t fifo; | 375 | phys_addr_t fifo; |
375 | struct psc_dma *psc_dma; | 376 | struct psc_dma *psc_dma; |
@@ -475,7 +476,7 @@ int mpc5200_audio_dma_create(struct of_device *op) | |||
475 | dev_set_drvdata(&op->dev, psc_dma); | 476 | dev_set_drvdata(&op->dev, psc_dma); |
476 | 477 | ||
477 | /* Tell the ASoC OF helpers about it */ | 478 | /* Tell the ASoC OF helpers about it */ |
478 | return snd_soc_register_platform(&mpc5200_audio_dma_platform); | 479 | return snd_soc_register_platform(&op->dev, &mpc5200_audio_dma_platform); |
479 | out_irq: | 480 | out_irq: |
480 | free_irq(psc_dma->irq, psc_dma); | 481 | free_irq(psc_dma->irq, psc_dma); |
481 | free_irq(psc_dma->capture.irq, &psc_dma->capture); | 482 | free_irq(psc_dma->capture.irq, &psc_dma->capture); |
@@ -486,15 +487,14 @@ out_unmap: | |||
486 | iounmap(regs); | 487 | iounmap(regs); |
487 | return ret; | 488 | return ret; |
488 | } | 489 | } |
489 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); | ||
490 | 490 | ||
491 | int mpc5200_audio_dma_destroy(struct of_device *op) | 491 | static int mpc5200_hpcd_remove(struct of_device *op) |
492 | { | 492 | { |
493 | struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); | 493 | struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); |
494 | 494 | ||
495 | dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); | 495 | dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); |
496 | 496 | ||
497 | snd_soc_unregister_platform(&mpc5200_audio_dma_platform); | 497 | snd_soc_unregister_platform(&op->dev); |
498 | 498 | ||
499 | bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); | 499 | bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); |
500 | bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); | 500 | bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); |
@@ -510,7 +510,35 @@ int mpc5200_audio_dma_destroy(struct of_device *op) | |||
510 | 510 | ||
511 | return 0; | 511 | return 0; |
512 | } | 512 | } |
513 | EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); | 513 | |
514 | static struct of_device_id mpc5200_hpcd_match[] = { | ||
515 | { | ||
516 | .compatible = "fsl,mpc5200-pcm", | ||
517 | }, | ||
518 | {} | ||
519 | }; | ||
520 | MODULE_DEVICE_TABLE(of, mpc5200_hpcd_match); | ||
521 | |||
522 | static struct of_platform_driver mpc5200_hpcd_of_driver = { | ||
523 | .owner = THIS_MODULE, | ||
524 | .name = "mpc5200-pcm-audio", | ||
525 | .match_table = mpc5200_hpcd_match, | ||
526 | .probe = mpc5200_hpcd_probe, | ||
527 | .remove = mpc5200_hpcd_remove, | ||
528 | }; | ||
529 | |||
530 | static int __init mpc5200_hpcd_init(void) | ||
531 | { | ||
532 | return of_register_platform_driver(&mpc5200_hpcd_of_driver); | ||
533 | } | ||
534 | |||
535 | static void __exit mpc5200_hpcd_exit(void) | ||
536 | { | ||
537 | of_unregister_platform_driver(&mpc5200_hpcd_of_driver); | ||
538 | } | ||
539 | |||
540 | module_init(mpc5200_hpcd_init); | ||
541 | module_exit(mpc5200_hpcd_exit); | ||
514 | 542 | ||
515 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | 543 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); |
516 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); | 544 | MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); |
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index 22208b373fb9..7472531bc2a4 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h | |||
@@ -81,9 +81,4 @@ to_psc_dma_stream(struct snd_pcm_substream *substream, struct psc_dma *psc_dma) | |||
81 | return &psc_dma->playback; | 81 | return &psc_dma->playback; |
82 | } | 82 | } |
83 | 83 | ||
84 | int mpc5200_audio_dma_create(struct of_device *op); | ||
85 | int mpc5200_audio_dma_destroy(struct of_device *op); | ||
86 | |||
87 | extern struct snd_soc_platform mpc5200_audio_dma_platform; | ||
88 | |||
89 | #endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ | 84 | #endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */ |
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index e2ee220bfb7e..11706c128c08 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c | |||
@@ -129,7 +129,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, | |||
129 | struct snd_pcm_hw_params *params, | 129 | struct snd_pcm_hw_params *params, |
130 | struct snd_soc_dai *cpu_dai) | 130 | struct snd_soc_dai *cpu_dai) |
131 | { | 131 | { |
132 | struct psc_dma *psc_dma = cpu_dai->private_data; | 132 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
133 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); | 133 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); |
134 | 134 | ||
135 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" | 135 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" |
@@ -152,7 +152,7 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, | |||
152 | struct snd_pcm_hw_params *params, | 152 | struct snd_pcm_hw_params *params, |
153 | struct snd_soc_dai *cpu_dai) | 153 | struct snd_soc_dai *cpu_dai) |
154 | { | 154 | { |
155 | struct psc_dma *psc_dma = cpu_dai->private_data; | 155 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
156 | 156 | ||
157 | dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); | 157 | dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); |
158 | 158 | ||
@@ -167,8 +167,7 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, | |||
167 | static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, | 167 | static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, |
168 | struct snd_soc_dai *dai) | 168 | struct snd_soc_dai *dai) |
169 | { | 169 | { |
170 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 170 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(dai); |
171 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
172 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); | 171 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); |
173 | 172 | ||
174 | switch (cmd) { | 173 | switch (cmd) { |
@@ -193,10 +192,9 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, | |||
193 | return 0; | 192 | return 0; |
194 | } | 193 | } |
195 | 194 | ||
196 | static int psc_ac97_probe(struct platform_device *pdev, | 195 | static int psc_ac97_probe(struct snd_soc_dai *cpu_dai) |
197 | struct snd_soc_dai *cpu_dai) | ||
198 | { | 196 | { |
199 | struct psc_dma *psc_dma = cpu_dai->private_data; | 197 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
200 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | 198 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; |
201 | 199 | ||
202 | /* Go */ | 200 | /* Go */ |
@@ -223,9 +221,8 @@ static struct snd_soc_dai_ops psc_ac97_digital_ops = { | |||
223 | .hw_params = psc_ac97_hw_digital_params, | 221 | .hw_params = psc_ac97_hw_digital_params, |
224 | }; | 222 | }; |
225 | 223 | ||
226 | struct snd_soc_dai psc_ac97_dai[] = { | 224 | static struct snd_soc_dai_driver psc_ac97_dai[] = { |
227 | { | 225 | { |
228 | .name = "AC97", | ||
229 | .ac97_control = 1, | 226 | .ac97_control = 1, |
230 | .probe = psc_ac97_probe, | 227 | .probe = psc_ac97_probe, |
231 | .playback = { | 228 | .playback = { |
@@ -243,7 +240,6 @@ struct snd_soc_dai psc_ac97_dai[] = { | |||
243 | .ops = &psc_ac97_analog_ops, | 240 | .ops = &psc_ac97_analog_ops, |
244 | }, | 241 | }, |
245 | { | 242 | { |
246 | .name = "SPDIF", | ||
247 | .ac97_control = 1, | 243 | .ac97_control = 1, |
248 | .playback = { | 244 | .playback = { |
249 | .channels_min = 1, | 245 | .channels_min = 1, |
@@ -254,7 +250,6 @@ struct snd_soc_dai psc_ac97_dai[] = { | |||
254 | }, | 250 | }, |
255 | .ops = &psc_ac97_digital_ops, | 251 | .ops = &psc_ac97_digital_ops, |
256 | } }; | 252 | } }; |
257 | EXPORT_SYMBOL_GPL(psc_ac97_dai); | ||
258 | 253 | ||
259 | 254 | ||
260 | 255 | ||
@@ -266,18 +261,11 @@ EXPORT_SYMBOL_GPL(psc_ac97_dai); | |||
266 | static int __devinit psc_ac97_of_probe(struct of_device *op, | 261 | static int __devinit psc_ac97_of_probe(struct of_device *op, |
267 | const struct of_device_id *match) | 262 | const struct of_device_id *match) |
268 | { | 263 | { |
269 | int rc, i; | 264 | int rc; |
270 | struct snd_ac97 ac97; | 265 | struct snd_ac97 ac97; |
271 | struct mpc52xx_psc __iomem *regs; | 266 | struct mpc52xx_psc __iomem *regs; |
272 | 267 | ||
273 | rc = mpc5200_audio_dma_create(op); | 268 | rc = snd_soc_register_dais(&op->dev, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); |
274 | if (rc != 0) | ||
275 | return rc; | ||
276 | |||
277 | for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) | ||
278 | psc_ac97_dai[i].dev = &op->dev; | ||
279 | |||
280 | rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); | ||
281 | if (rc != 0) { | 269 | if (rc != 0) { |
282 | dev_err(&op->dev, "Failed to register DAI\n"); | 270 | dev_err(&op->dev, "Failed to register DAI\n"); |
283 | return rc; | 271 | return rc; |
@@ -287,9 +275,6 @@ static int __devinit psc_ac97_of_probe(struct of_device *op, | |||
287 | regs = psc_dma->psc_regs; | 275 | regs = psc_dma->psc_regs; |
288 | ac97.private_data = psc_dma; | 276 | ac97.private_data = psc_dma; |
289 | 277 | ||
290 | for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) | ||
291 | psc_ac97_dai[i].private_data = psc_dma; | ||
292 | |||
293 | psc_dma->imr = 0; | 278 | psc_dma->imr = 0; |
294 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | 279 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); |
295 | 280 | ||
@@ -305,7 +290,8 @@ static int __devinit psc_ac97_of_probe(struct of_device *op, | |||
305 | 290 | ||
306 | static int __devexit psc_ac97_of_remove(struct of_device *op) | 291 | static int __devexit psc_ac97_of_remove(struct of_device *op) |
307 | { | 292 | { |
308 | return mpc5200_audio_dma_destroy(op); | 293 | snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_ac97_dai)); |
294 | return 0; | ||
309 | } | 295 | } |
310 | 296 | ||
311 | /* Match table for of_platform binding */ | 297 | /* Match table for of_platform binding */ |
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h index 4bc18c35c369..e881e784b270 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.h +++ b/sound/soc/fsl/mpc5200_psc_ac97.h | |||
@@ -7,8 +7,6 @@ | |||
7 | #ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ | 7 | #ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ |
8 | #define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ | 8 | #define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ |
9 | 9 | ||
10 | extern struct snd_soc_dai psc_ac97_dai[]; | ||
11 | |||
12 | #define MPC5200_AC97_NORMAL 0 | 10 | #define MPC5200_AC97_NORMAL 0 |
13 | #define MPC5200_AC97_SPDIF 1 | 11 | #define MPC5200_AC97_SPDIF 1 |
14 | 12 | ||
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 676841cbae98..5b9f2c73f031 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c | |||
@@ -40,7 +40,7 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | |||
40 | struct snd_soc_dai *dai) | 40 | struct snd_soc_dai *dai) |
41 | { | 41 | { |
42 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 42 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
43 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | 43 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
44 | u32 mode; | 44 | u32 mode; |
45 | 45 | ||
46 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" | 46 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" |
@@ -88,7 +88,7 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream, | |||
88 | static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | 88 | static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, |
89 | int clk_id, unsigned int freq, int dir) | 89 | int clk_id, unsigned int freq, int dir) |
90 | { | 90 | { |
91 | struct psc_dma *psc_dma = cpu_dai->private_data; | 91 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
92 | dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", | 92 | dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", |
93 | cpu_dai, dir); | 93 | cpu_dai, dir); |
94 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; | 94 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; |
@@ -107,7 +107,7 @@ static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | |||
107 | */ | 107 | */ |
108 | static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) | 108 | static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) |
109 | { | 109 | { |
110 | struct psc_dma *psc_dma = cpu_dai->private_data; | 110 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
111 | dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", | 111 | dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", |
112 | cpu_dai, format); | 112 | cpu_dai, format); |
113 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; | 113 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; |
@@ -129,8 +129,7 @@ static struct snd_soc_dai_ops psc_i2s_dai_ops = { | |||
129 | .set_fmt = psc_i2s_set_fmt, | 129 | .set_fmt = psc_i2s_set_fmt, |
130 | }; | 130 | }; |
131 | 131 | ||
132 | struct snd_soc_dai psc_i2s_dai[] = {{ | 132 | static struct snd_soc_dai_driver psc_i2s_dai[] = {{ |
133 | .name = "I2S", | ||
134 | .playback = { | 133 | .playback = { |
135 | .channels_min = 2, | 134 | .channels_min = 2, |
136 | .channels_max = 2, | 135 | .channels_max = 2, |
@@ -145,7 +144,6 @@ struct snd_soc_dai psc_i2s_dai[] = {{ | |||
145 | }, | 144 | }, |
146 | .ops = &psc_i2s_dai_ops, | 145 | .ops = &psc_i2s_dai_ops, |
147 | } }; | 146 | } }; |
148 | EXPORT_SYMBOL_GPL(psc_i2s_dai); | ||
149 | 147 | ||
150 | /* --------------------------------------------------------------------- | 148 | /* --------------------------------------------------------------------- |
151 | * OF platform bus binding code: | 149 | * OF platform bus binding code: |
@@ -159,11 +157,7 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, | |||
159 | struct psc_dma *psc_dma; | 157 | struct psc_dma *psc_dma; |
160 | struct mpc52xx_psc __iomem *regs; | 158 | struct mpc52xx_psc __iomem *regs; |
161 | 159 | ||
162 | rc = mpc5200_audio_dma_create(op); | 160 | rc = snd_soc_register_dais(&op->dev, psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); |
163 | if (rc != 0) | ||
164 | return rc; | ||
165 | |||
166 | rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); | ||
167 | if (rc != 0) { | 161 | if (rc != 0) { |
168 | pr_err("Failed to register DAI\n"); | 162 | pr_err("Failed to register DAI\n"); |
169 | return 0; | 163 | return 0; |
@@ -207,7 +201,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op, | |||
207 | 201 | ||
208 | static int __devexit psc_i2s_of_remove(struct of_device *op) | 202 | static int __devexit psc_i2s_of_remove(struct of_device *op) |
209 | { | 203 | { |
210 | return mpc5200_audio_dma_destroy(op); | 204 | snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_i2s_dai)); |
205 | return 0; | ||
211 | } | 206 | } |
212 | 207 | ||
213 | /* Match table for of_platform binding */ | 208 | /* Match table for of_platform binding */ |
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 6a2764ee8203..5ba823213abe 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -1,85 +1,96 @@ | |||
1 | /** | 1 | /** |
2 | * Freescale MPC8610HPCD ALSA SoC Fabric driver | 2 | * Freescale MPC8610HPCD ALSA SoC Machine driver |
3 | * | 3 | * |
4 | * Author: Timur Tabi <timur@freescale.com> | 4 | * Author: Timur Tabi <timur@freescale.com> |
5 | * | 5 | * |
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | 6 | * Copyright 2007-2010 Freescale Semiconductor, Inc. |
7 | * under the terms of the GNU General Public License version 2. This | 7 | * |
8 | * program is licensed "as is" without any warranty of any kind, whether | 8 | * This file is licensed under the terms of the GNU General Public License |
9 | * express or implied. | 9 | * version 2. This program is licensed "as is" without any warranty of any |
10 | * kind, whether express or implied. | ||
10 | */ | 11 | */ |
11 | 12 | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
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/of_platform.h> | ||
17 | #include <sound/soc.h> | 16 | #include <sound/soc.h> |
18 | #include <asm/immap_86xx.h> | 17 | #include <asm/immap_86xx.h> |
19 | 18 | ||
20 | #include "../codecs/cs4270.h" | ||
21 | #include "fsl_dma.h" | 19 | #include "fsl_dma.h" |
22 | #include "fsl_ssi.h" | 20 | #include "fsl_ssi.h" |
23 | 21 | ||
22 | /* There's only one global utilities register */ | ||
23 | static phys_addr_t guts_phys; | ||
24 | |||
25 | #define DAI_NAME_SIZE 32 | ||
26 | |||
24 | /** | 27 | /** |
25 | * mpc8610_hpcd_data: fabric-specific ASoC device data | 28 | * mpc8610_hpcd_data: machine-specific ASoC device data |
26 | * | 29 | * |
27 | * This structure contains data for a single sound platform device on an | 30 | * This structure contains data for a single sound platform device on an |
28 | * MPC8610 HPCD. Some of the data is taken from the device tree. | 31 | * MPC8610 HPCD. Some of the data is taken from the device tree. |
29 | */ | 32 | */ |
30 | struct mpc8610_hpcd_data { | 33 | struct mpc8610_hpcd_data { |
31 | struct snd_soc_device sound_devdata; | 34 | struct snd_soc_dai_link dai[2]; |
32 | struct snd_soc_dai_link dai; | 35 | struct snd_soc_card card; |
33 | struct snd_soc_card machine; | ||
34 | unsigned int dai_format; | 36 | unsigned int dai_format; |
35 | unsigned int codec_clk_direction; | 37 | unsigned int codec_clk_direction; |
36 | unsigned int cpu_clk_direction; | 38 | unsigned int cpu_clk_direction; |
37 | unsigned int clk_frequency; | 39 | unsigned int clk_frequency; |
38 | struct ccsr_guts __iomem *guts; | 40 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ |
39 | struct ccsr_ssi __iomem *ssi; | 41 | unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ |
40 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ | ||
41 | unsigned int ssi_irq; | ||
42 | unsigned int dma_id; /* 0 = DMA1, 1 = DMA2, etc */ | ||
43 | unsigned int dma_irq[2]; | ||
44 | struct ccsr_dma_channel __iomem *dma[2]; | ||
45 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ | 42 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ |
43 | char codec_dai_name[DAI_NAME_SIZE]; | ||
44 | char codec_name[DAI_NAME_SIZE]; | ||
45 | char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ | ||
46 | }; | 46 | }; |
47 | 47 | ||
48 | /** | 48 | /** |
49 | * mpc8610_hpcd_machine_probe: initalize the board | 49 | * mpc8610_hpcd_machine_probe: initialize the board |
50 | * | 50 | * |
51 | * This function is called when platform_device_add() is called. It is used | 51 | * This function is used to initialize the board-specific hardware. |
52 | * to initialize the board-specific hardware. | ||
53 | * | 52 | * |
54 | * Here we program the DMACR and PMUXCR registers. | 53 | * Here we program the DMACR and PMUXCR registers. |
55 | */ | 54 | */ |
56 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | 55 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) |
57 | { | 56 | { |
57 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
58 | struct mpc8610_hpcd_data *machine_data = | 58 | struct mpc8610_hpcd_data *machine_data = |
59 | sound_device->dev.platform_data; | 59 | container_of(card, struct mpc8610_hpcd_data, card); |
60 | struct ccsr_guts __iomem *guts; | ||
60 | 61 | ||
61 | /* Program the signal routing between the SSI and the DMA */ | 62 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); |
62 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 63 | if (!guts) { |
63 | machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); | 64 | dev_err(card->dev, "could not map global utilities\n"); |
64 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 65 | return -ENOMEM; |
65 | machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); | 66 | } |
66 | 67 | ||
67 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 68 | /* Program the signal routing between the SSI and the DMA */ |
68 | machine_data->dma_channel_id[0], 0); | 69 | guts_set_dmacr(guts, machine_data->dma_id[0], |
69 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 70 | machine_data->dma_channel_id[0], |
70 | machine_data->dma_channel_id[1], 0); | 71 | CCSR_GUTS_DMACR_DEV_SSI); |
72 | guts_set_dmacr(guts, machine_data->dma_id[1], | ||
73 | machine_data->dma_channel_id[1], | ||
74 | CCSR_GUTS_DMACR_DEV_SSI); | ||
75 | |||
76 | guts_set_pmuxcr_dma(guts, machine_data->dma_id[0], | ||
77 | machine_data->dma_channel_id[0], 0); | ||
78 | guts_set_pmuxcr_dma(guts, machine_data->dma_id[1], | ||
79 | machine_data->dma_channel_id[1], 0); | ||
71 | 80 | ||
72 | switch (machine_data->ssi_id) { | 81 | switch (machine_data->ssi_id) { |
73 | case 0: | 82 | case 0: |
74 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 83 | clrsetbits_be32(&guts->pmuxcr, |
75 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); | 84 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); |
76 | break; | 85 | break; |
77 | case 1: | 86 | case 1: |
78 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 87 | clrsetbits_be32(&guts->pmuxcr, |
79 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); | 88 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); |
80 | break; | 89 | break; |
81 | } | 90 | } |
82 | 91 | ||
92 | iounmap(guts); | ||
93 | |||
83 | return 0; | 94 | return 0; |
84 | } | 95 | } |
85 | 96 | ||
@@ -93,38 +104,15 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | |||
93 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | 104 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) |
94 | { | 105 | { |
95 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 106 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
96 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
97 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
98 | struct mpc8610_hpcd_data *machine_data = | 107 | struct mpc8610_hpcd_data *machine_data = |
99 | rtd->socdev->dev->platform_data; | 108 | container_of(rtd->card, struct mpc8610_hpcd_data, card); |
109 | struct device *dev = rtd->card->dev; | ||
100 | int ret = 0; | 110 | int ret = 0; |
101 | 111 | ||
102 | /* Tell the CPU driver what the serial protocol is. */ | ||
103 | ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format); | ||
104 | if (ret < 0) { | ||
105 | dev_err(substream->pcm->card->dev, | ||
106 | "could not set CPU driver audio format\n"); | ||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | /* Tell the codec driver what the serial protocol is. */ | 112 | /* Tell the codec driver what the serial protocol is. */ |
111 | ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format); | 113 | ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format); |
112 | if (ret < 0) { | 114 | if (ret < 0) { |
113 | dev_err(substream->pcm->card->dev, | 115 | dev_err(dev, "could not set codec driver audio format\n"); |
114 | "could not set codec driver audio format\n"); | ||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Tell the CPU driver what the clock frequency is, and whether it's a | ||
120 | * slave or master. | ||
121 | */ | ||
122 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, | ||
123 | machine_data->clk_frequency, | ||
124 | machine_data->cpu_clk_direction); | ||
125 | if (ret < 0) { | ||
126 | dev_err(substream->pcm->card->dev, | ||
127 | "could not set CPU driver clock parameters\n"); | ||
128 | return ret; | 116 | return ret; |
129 | } | 117 | } |
130 | 118 | ||
@@ -132,12 +120,11 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | |||
132 | * Tell the codec driver what the MCLK frequency is, and whether it's | 120 | * Tell the codec driver what the MCLK frequency is, and whether it's |
133 | * a slave or master. | 121 | * a slave or master. |
134 | */ | 122 | */ |
135 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, | 123 | ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, |
136 | machine_data->clk_frequency, | 124 | machine_data->clk_frequency, |
137 | machine_data->codec_clk_direction); | 125 | machine_data->codec_clk_direction); |
138 | if (ret < 0) { | 126 | if (ret < 0) { |
139 | dev_err(substream->pcm->card->dev, | 127 | dev_err(dev, "could not set codec driver clock params\n"); |
140 | "could not set codec driver clock params\n"); | ||
141 | return ret; | 128 | return ret; |
142 | } | 129 | } |
143 | 130 | ||
@@ -150,116 +137,254 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | |||
150 | * This function is called to remove the sound device for one SSI. We | 137 | * This function is called to remove the sound device for one SSI. We |
151 | * de-program the DMACR and PMUXCR register. | 138 | * de-program the DMACR and PMUXCR register. |
152 | */ | 139 | */ |
153 | int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) | 140 | static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) |
154 | { | 141 | { |
142 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
155 | struct mpc8610_hpcd_data *machine_data = | 143 | struct mpc8610_hpcd_data *machine_data = |
156 | sound_device->dev.platform_data; | 144 | container_of(card, struct mpc8610_hpcd_data, card); |
145 | struct ccsr_guts __iomem *guts; | ||
146 | |||
147 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); | ||
148 | if (!guts) { | ||
149 | dev_err(card->dev, "could not map global utilities\n"); | ||
150 | return -ENOMEM; | ||
151 | } | ||
157 | 152 | ||
158 | /* Restore the signal routing */ | 153 | /* Restore the signal routing */ |
159 | 154 | ||
160 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 155 | guts_set_dmacr(guts, machine_data->dma_id[0], |
161 | machine_data->dma_channel_id[0], 0); | 156 | machine_data->dma_channel_id[0], 0); |
162 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 157 | guts_set_dmacr(guts, machine_data->dma_id[1], |
163 | machine_data->dma_channel_id[1], 0); | 158 | machine_data->dma_channel_id[1], 0); |
164 | 159 | ||
165 | switch (machine_data->ssi_id) { | 160 | switch (machine_data->ssi_id) { |
166 | case 0: | 161 | case 0: |
167 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 162 | clrsetbits_be32(&guts->pmuxcr, |
168 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); | 163 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); |
169 | break; | 164 | break; |
170 | case 1: | 165 | case 1: |
171 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 166 | clrsetbits_be32(&guts->pmuxcr, |
172 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); | 167 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); |
173 | break; | 168 | break; |
174 | } | 169 | } |
175 | 170 | ||
171 | iounmap(guts); | ||
172 | |||
176 | return 0; | 173 | return 0; |
177 | } | 174 | } |
178 | 175 | ||
179 | /** | 176 | /** |
180 | * mpc8610_hpcd_ops: ASoC fabric driver operations | 177 | * mpc8610_hpcd_ops: ASoC machine driver operations |
181 | */ | 178 | */ |
182 | static struct snd_soc_ops mpc8610_hpcd_ops = { | 179 | static struct snd_soc_ops mpc8610_hpcd_ops = { |
183 | .startup = mpc8610_hpcd_startup, | 180 | .startup = mpc8610_hpcd_startup, |
184 | }; | 181 | }; |
185 | 182 | ||
186 | /** | 183 | /** |
187 | * mpc8610_hpcd_probe: OF probe function for the fabric driver | 184 | * get_node_by_phandle_name - get a node by its phandle name |
188 | * | 185 | * |
189 | * This function gets called when an SSI node is found in the device tree. | 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. | ||
190 | * | 189 | * |
191 | * Although this is a fabric driver, the SSI node is the "master" node with | 190 | * If the property is not a phandle, or the node it points to is not compatible |
192 | * respect to audio hardware connections. Therefore, we create a new ASoC | 191 | * with the specific string, then NULL is returned. |
193 | * device for each new SSI node that has a codec attached. | 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 | ||
194 | * | 218 | * |
195 | * FIXME: Currently, we only support one DMA controller, so if there are | 219 | * Return the value of the cell-index property of the parent of the given |
196 | * multiple SSI nodes with codecs, only the first will be supported. | 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 *iprop; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * codec_node_dev_name - determine the dev_name for a codec node | ||
197 | * | 242 | * |
198 | * FIXME: Even if we did support multiple DMA controllers, we have no | 243 | * This function determines the dev_name for an I2C node. This is the name |
199 | * mechanism for assigning DMA controllers and channels to the individual | 244 | * that would be returned by dev_name() if this device_node were part of a |
200 | * SSI devices. We also probably aren't compatible with the generic Elo DMA | 245 | * 'struct device' It's ugly and hackish, but it works. |
201 | * device driver. | 246 | * |
247 | * The dev_name for such devices include the bus number and I2C address. For | ||
248 | * example, "cs4270-codec.0-004f". | ||
202 | */ | 249 | */ |
203 | static int mpc8610_hpcd_probe(struct of_device *ofdev, | 250 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) |
204 | const struct of_device_id *match) | ||
205 | { | 251 | { |
206 | struct device_node *np = ofdev->dev.of_node; | ||
207 | struct device_node *codec_np = NULL; | ||
208 | struct device_node *guts_np = NULL; | ||
209 | struct device_node *dma_np = NULL; | ||
210 | struct device_node *dma_channel_np = NULL; | ||
211 | const phandle *codec_ph; | ||
212 | const char *sprop; | ||
213 | const u32 *iprop; | 252 | const u32 *iprop; |
253 | int bus, addr; | ||
254 | char temp[DAI_NAME_SIZE]; | ||
255 | |||
256 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
257 | |||
258 | iprop = of_get_property(np, "reg", NULL); | ||
259 | if (!iprop) | ||
260 | return -EINVAL; | ||
261 | |||
262 | addr = *iprop; | ||
263 | |||
264 | bus = get_parent_cell_index(np); | ||
265 | if (bus < 0) | ||
266 | return bus; | ||
267 | |||
268 | snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int get_dma_channel(struct device_node *ssi_np, | ||
274 | const char *compatible, | ||
275 | struct snd_soc_dai_link *dai, | ||
276 | unsigned int *dma_channel_id, | ||
277 | unsigned int *dma_id) | ||
278 | { | ||
214 | struct resource res; | 279 | struct resource res; |
280 | struct device_node *dma_channel_np; | ||
281 | const u32 *iprop; | ||
282 | int ret; | ||
283 | |||
284 | dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, | ||
285 | "fsl,ssi-dma-channel"); | ||
286 | if (!dma_channel_np) | ||
287 | return -EINVAL; | ||
288 | |||
289 | /* Determine the dev_name for the device_node. This code mimics the | ||
290 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
291 | * the dev_name() of the device to match the platform (DMA) device with | ||
292 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
293 | * now). | ||
294 | * | ||
295 | * dai->platform name should already point to an allocated buffer. | ||
296 | */ | ||
297 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
298 | if (ret) | ||
299 | return ret; | ||
300 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
301 | (unsigned long long) res.start, dma_channel_np->name); | ||
302 | |||
303 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
304 | if (!iprop) { | ||
305 | of_node_put(dma_channel_np); | ||
306 | return -EINVAL; | ||
307 | } | ||
308 | |||
309 | *dma_channel_id = *iprop; | ||
310 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
311 | of_node_put(dma_channel_np); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * mpc8610_hpcd_probe: platform probe function for the machine driver | ||
318 | * | ||
319 | * Although this is a machine driver, the SSI node is the "master" node with | ||
320 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
321 | * device for each new SSI node that has a codec attached. | ||
322 | */ | ||
323 | static int mpc8610_hpcd_probe(struct platform_device *pdev) | ||
324 | { | ||
325 | struct device *dev = pdev->dev.parent; | ||
326 | /* of_dev is the OF device for the SSI node that probed us */ | ||
327 | struct of_device *of_dev = container_of(dev, struct of_device, dev); | ||
328 | struct device_node *np = of_dev->dev.of_node; | ||
329 | struct device_node *codec_np = NULL; | ||
215 | struct platform_device *sound_device = NULL; | 330 | struct platform_device *sound_device = NULL; |
216 | struct mpc8610_hpcd_data *machine_data; | 331 | struct mpc8610_hpcd_data *machine_data; |
217 | struct fsl_ssi_info ssi_info; | ||
218 | struct fsl_dma_info dma_info; | ||
219 | int ret = -ENODEV; | 332 | int ret = -ENODEV; |
220 | unsigned int playback_dma_channel; | 333 | const char *sprop; |
221 | unsigned int capture_dma_channel; | 334 | const u32 *iprop; |
335 | |||
336 | /* We are only interested in SSIs with a codec phandle in them, | ||
337 | * so let's make sure this SSI has one. The MPC8610 HPCD only | ||
338 | * knows about the CS4270 codec, so reject anything else. | ||
339 | */ | ||
340 | codec_np = get_node_by_phandle_name(np, "codec-handle", | ||
341 | "cirrus,cs4270"); | ||
342 | if (!codec_np) { | ||
343 | dev_err(dev, "invalid codec node\n"); | ||
344 | return -EINVAL; | ||
345 | } | ||
222 | 346 | ||
223 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); | 347 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); |
224 | if (!machine_data) | 348 | if (!machine_data) |
225 | return -ENOMEM; | 349 | return -ENOMEM; |
226 | 350 | ||
227 | memset(&ssi_info, 0, sizeof(ssi_info)); | 351 | machine_data->dai[0].cpu_dai_name = dev_name(&of_dev->dev); |
228 | memset(&dma_info, 0, sizeof(dma_info)); | 352 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; |
229 | 353 | ||
230 | ssi_info.dev = &ofdev->dev; | 354 | /* Determine the codec name, it will be used as the codec DAI name */ |
231 | 355 | ret = codec_node_dev_name(codec_np, machine_data->codec_name, | |
232 | /* | 356 | DAI_NAME_SIZE); |
233 | * We are only interested in SSIs with a codec phandle in them, so let's | 357 | if (ret) { |
234 | * make sure this SSI has one. | 358 | dev_err(&pdev->dev, "invalid codec node %s\n", |
235 | */ | 359 | codec_np->full_name); |
236 | codec_ph = of_get_property(np, "codec-handle", NULL); | 360 | ret = -EINVAL; |
237 | if (!codec_ph) | ||
238 | goto error; | 361 | goto error; |
362 | } | ||
363 | machine_data->dai[0].codec_name = machine_data->codec_name; | ||
239 | 364 | ||
240 | codec_np = of_find_node_by_phandle(*codec_ph); | 365 | /* The DAI name from the codec (snd_soc_dai_driver.name) */ |
241 | if (!codec_np) | 366 | machine_data->dai[0].codec_dai_name = "cs4270-hifi"; |
242 | goto error; | ||
243 | 367 | ||
244 | /* The MPC8610 HPCD only knows about the CS4270 codec, so reject | 368 | /* We register two DAIs per SSI, one for playback and the other for |
245 | anything else. */ | 369 | * capture. Currently, we only support codecs that have one DAI for |
246 | if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) | 370 | * both playback and capture. |
247 | goto error; | 371 | */ |
372 | memcpy(&machine_data->dai[1], &machine_data->dai[0], | ||
373 | sizeof(struct snd_soc_dai_link)); | ||
248 | 374 | ||
249 | /* Get the device ID */ | 375 | /* Get the device ID */ |
250 | iprop = of_get_property(np, "cell-index", NULL); | 376 | iprop = of_get_property(np, "cell-index", NULL); |
251 | if (!iprop) { | 377 | if (!iprop) { |
252 | dev_err(&ofdev->dev, "cell-index property not found\n"); | 378 | dev_err(&pdev->dev, "cell-index property not found\n"); |
253 | ret = -EINVAL; | 379 | ret = -EINVAL; |
254 | goto error; | 380 | goto error; |
255 | } | 381 | } |
256 | machine_data->ssi_id = *iprop; | 382 | machine_data->ssi_id = *iprop; |
257 | ssi_info.id = *iprop; | ||
258 | 383 | ||
259 | /* Get the serial format and clock direction. */ | 384 | /* Get the serial format and clock direction. */ |
260 | sprop = of_get_property(np, "fsl,mode", NULL); | 385 | sprop = of_get_property(np, "fsl,mode", NULL); |
261 | if (!sprop) { | 386 | if (!sprop) { |
262 | dev_err(&ofdev->dev, "fsl,mode property not found\n"); | 387 | dev_err(&pdev->dev, "fsl,mode property not found\n"); |
263 | ret = -EINVAL; | 388 | ret = -EINVAL; |
264 | goto error; | 389 | goto error; |
265 | } | 390 | } |
@@ -269,15 +394,14 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
269 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | 394 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; |
270 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | 395 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; |
271 | 396 | ||
272 | /* | 397 | /* In i2s-slave mode, the codec has its own clock source, so we |
273 | * In i2s-slave mode, the codec has its own clock source, so we | ||
274 | * need to get the frequency from the device tree and pass it to | 398 | * need to get the frequency from the device tree and pass it to |
275 | * the codec driver. | 399 | * the codec driver. |
276 | */ | 400 | */ |
277 | iprop = of_get_property(codec_np, "clock-frequency", NULL); | 401 | iprop = of_get_property(codec_np, "clock-frequency", NULL); |
278 | if (!iprop || !*iprop) { | 402 | if (!iprop || !*iprop) { |
279 | dev_err(&ofdev->dev, "codec bus-frequency property " | 403 | dev_err(&pdev->dev, "codec bus-frequency " |
280 | "is missing or invalid\n"); | 404 | "property is missing or invalid\n"); |
281 | ret = -EINVAL; | 405 | ret = -EINVAL; |
282 | goto error; | 406 | goto error; |
283 | } | 407 | } |
@@ -311,317 +435,153 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
311 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | 435 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; |
312 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | 436 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; |
313 | } else { | 437 | } else { |
314 | dev_err(&ofdev->dev, | 438 | dev_err(&pdev->dev, |
315 | "unrecognized fsl,mode property \"%s\"\n", sprop); | 439 | "unrecognized fsl,mode property '%s'\n", sprop); |
316 | ret = -EINVAL; | 440 | ret = -EINVAL; |
317 | goto error; | 441 | goto error; |
318 | } | 442 | } |
319 | 443 | ||
320 | if (!machine_data->clk_frequency) { | 444 | if (!machine_data->clk_frequency) { |
321 | dev_err(&ofdev->dev, "unknown clock frequency\n"); | 445 | dev_err(&pdev->dev, "unknown clock frequency\n"); |
322 | ret = -EINVAL; | 446 | ret = -EINVAL; |
323 | goto error; | 447 | goto error; |
324 | } | 448 | } |
325 | 449 | ||
326 | /* Read the SSI information from the device tree */ | 450 | /* Find the playback DMA channel to use. */ |
327 | ret = of_address_to_resource(np, 0, &res); | 451 | machine_data->dai[0].platform_name = machine_data->platform_name[0]; |
452 | ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], | ||
453 | &machine_data->dma_channel_id[0], | ||
454 | &machine_data->dma_id[0]); | ||
328 | if (ret) { | 455 | if (ret) { |
329 | dev_err(&ofdev->dev, "could not obtain SSI address\n"); | 456 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); |
330 | goto error; | ||
331 | } | ||
332 | if (!res.start) { | ||
333 | dev_err(&ofdev->dev, "invalid SSI address\n"); | ||
334 | goto error; | ||
335 | } | ||
336 | ssi_info.ssi_phys = res.start; | ||
337 | |||
338 | machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi)); | ||
339 | if (!machine_data->ssi) { | ||
340 | dev_err(&ofdev->dev, "could not map SSI address %x\n", | ||
341 | ssi_info.ssi_phys); | ||
342 | ret = -EINVAL; | ||
343 | goto error; | ||
344 | } | ||
345 | ssi_info.ssi = machine_data->ssi; | ||
346 | |||
347 | |||
348 | /* Get the IRQ of the SSI */ | ||
349 | machine_data->ssi_irq = irq_of_parse_and_map(np, 0); | ||
350 | if (!machine_data->ssi_irq) { | ||
351 | dev_err(&ofdev->dev, "could not get SSI IRQ\n"); | ||
352 | ret = -EINVAL; | ||
353 | goto error; | ||
354 | } | ||
355 | ssi_info.irq = machine_data->ssi_irq; | ||
356 | |||
357 | /* Do we want to use asynchronous mode? */ | ||
358 | ssi_info.asynchronous = | ||
359 | of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0; | ||
360 | if (ssi_info.asynchronous) | ||
361 | dev_info(&ofdev->dev, "using asynchronous mode\n"); | ||
362 | |||
363 | /* Map the global utilities registers. */ | ||
364 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); | ||
365 | if (!guts_np) { | ||
366 | dev_err(&ofdev->dev, "could not obtain address of GUTS\n"); | ||
367 | ret = -EINVAL; | ||
368 | goto error; | ||
369 | } | ||
370 | machine_data->guts = of_iomap(guts_np, 0); | ||
371 | of_node_put(guts_np); | ||
372 | if (!machine_data->guts) { | ||
373 | dev_err(&ofdev->dev, "could not map GUTS\n"); | ||
374 | ret = -EINVAL; | ||
375 | goto error; | ||
376 | } | ||
377 | |||
378 | /* Find the DMA channels to use. Both SSIs need to use the same DMA | ||
379 | * controller, so let's use DMA#1. | ||
380 | */ | ||
381 | for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { | ||
382 | iprop = of_get_property(dma_np, "cell-index", NULL); | ||
383 | if (iprop && (*iprop == 0)) { | ||
384 | of_node_put(dma_np); | ||
385 | break; | ||
386 | } | ||
387 | } | ||
388 | if (!dma_np) { | ||
389 | dev_err(&ofdev->dev, "could not find DMA node\n"); | ||
390 | ret = -EINVAL; | ||
391 | goto error; | ||
392 | } | ||
393 | machine_data->dma_id = *iprop; | ||
394 | |||
395 | /* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA | ||
396 | * channels 2 and 3. This is just how the MPC8610 is wired | ||
397 | * internally. | ||
398 | */ | ||
399 | playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2; | ||
400 | capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3; | ||
401 | |||
402 | /* | ||
403 | * Find the DMA channels to use. | ||
404 | */ | ||
405 | while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { | ||
406 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
407 | if (iprop && (*iprop == playback_dma_channel)) { | ||
408 | /* dma_channel[0] and dma_irq[0] are for playback */ | ||
409 | dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); | ||
410 | dma_info.dma_irq[0] = | ||
411 | irq_of_parse_and_map(dma_channel_np, 0); | ||
412 | machine_data->dma_channel_id[0] = *iprop; | ||
413 | continue; | ||
414 | } | ||
415 | if (iprop && (*iprop == capture_dma_channel)) { | ||
416 | /* dma_channel[1] and dma_irq[1] are for capture */ | ||
417 | dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); | ||
418 | dma_info.dma_irq[1] = | ||
419 | irq_of_parse_and_map(dma_channel_np, 0); | ||
420 | machine_data->dma_channel_id[1] = *iprop; | ||
421 | continue; | ||
422 | } | ||
423 | } | ||
424 | if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] || | ||
425 | !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) { | ||
426 | dev_err(&ofdev->dev, "could not find DMA channels\n"); | ||
427 | ret = -EINVAL; | ||
428 | goto error; | 457 | goto error; |
429 | } | 458 | } |
430 | 459 | ||
431 | dma_info.ssi_stx_phys = ssi_info.ssi_phys + | 460 | /* Find the capture DMA channel to use. */ |
432 | offsetof(struct ccsr_ssi, stx0); | 461 | machine_data->dai[1].platform_name = machine_data->platform_name[1]; |
433 | dma_info.ssi_srx_phys = ssi_info.ssi_phys + | 462 | ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], |
434 | offsetof(struct ccsr_ssi, srx0); | 463 | &machine_data->dma_channel_id[1], |
435 | 464 | &machine_data->dma_id[1]); | |
436 | /* We have the DMA information, so tell the DMA driver what it is */ | 465 | if (ret) { |
437 | if (!fsl_dma_configure(&dma_info)) { | 466 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); |
438 | dev_err(&ofdev->dev, "could not instantiate DMA device\n"); | ||
439 | ret = -EBUSY; | ||
440 | goto error; | 467 | goto error; |
441 | } | 468 | } |
442 | 469 | ||
443 | /* | 470 | /* Initialize our DAI data structure. */ |
444 | * Initialize our DAI data structure. We should probably get this | 471 | machine_data->dai[0].stream_name = "playback"; |
445 | * information from the device tree. | 472 | machine_data->dai[1].stream_name = "capture"; |
446 | */ | 473 | machine_data->dai[0].name = machine_data->dai[0].stream_name; |
447 | machine_data->dai.name = "CS4270"; | 474 | machine_data->dai[1].name = machine_data->dai[1].stream_name; |
448 | machine_data->dai.stream_name = "CS4270"; | ||
449 | |||
450 | machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info); | ||
451 | machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */ | ||
452 | machine_data->dai.ops = &mpc8610_hpcd_ops; | ||
453 | 475 | ||
454 | machine_data->machine.probe = mpc8610_hpcd_machine_probe; | 476 | machine_data->card.probe = mpc8610_hpcd_machine_probe; |
455 | machine_data->machine.remove = mpc8610_hpcd_machine_remove; | 477 | machine_data->card.remove = mpc8610_hpcd_machine_remove; |
456 | machine_data->machine.name = "MPC8610 HPCD"; | 478 | machine_data->card.name = pdev->name; /* The platform driver name */ |
457 | machine_data->machine.num_links = 1; | 479 | machine_data->card.num_links = 2; |
458 | machine_data->machine.dai_link = &machine_data->dai; | 480 | machine_data->card.dai_link = machine_data->dai; |
459 | 481 | ||
460 | /* Allocate a new audio platform device structure */ | 482 | /* Allocate a new audio platform device structure */ |
461 | sound_device = platform_device_alloc("soc-audio", -1); | 483 | sound_device = platform_device_alloc("soc-audio", -1); |
462 | if (!sound_device) { | 484 | if (!sound_device) { |
463 | dev_err(&ofdev->dev, "platform device allocation failed\n"); | 485 | dev_err(&pdev->dev, "platform device alloc failed\n"); |
464 | ret = -ENOMEM; | 486 | ret = -ENOMEM; |
465 | goto error; | 487 | goto error; |
466 | } | 488 | } |
467 | 489 | ||
468 | machine_data->sound_devdata.card = &machine_data->machine; | 490 | /* Associate the card data with the sound device */ |
469 | machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; | 491 | platform_set_drvdata(sound_device, &machine_data->card); |
470 | machine_data->machine.platform = &fsl_soc_platform; | ||
471 | |||
472 | sound_device->dev.platform_data = machine_data; | ||
473 | |||
474 | 492 | ||
475 | /* Set the platform device and ASoC device to point to each other */ | 493 | /* Register with ASoC */ |
476 | platform_set_drvdata(sound_device, &machine_data->sound_devdata); | ||
477 | |||
478 | machine_data->sound_devdata.dev = &sound_device->dev; | ||
479 | |||
480 | |||
481 | /* Tell ASoC to probe us. This will call mpc8610_hpcd_machine.probe(), | ||
482 | if it exists. */ | ||
483 | ret = platform_device_add(sound_device); | 494 | ret = platform_device_add(sound_device); |
484 | |||
485 | if (ret) { | 495 | if (ret) { |
486 | dev_err(&ofdev->dev, "platform device add failed\n"); | 496 | dev_err(&pdev->dev, "platform device add failed\n"); |
487 | goto error; | 497 | goto error; |
488 | } | 498 | } |
489 | 499 | ||
490 | dev_set_drvdata(&ofdev->dev, sound_device); | 500 | of_node_put(codec_np); |
491 | 501 | ||
492 | return 0; | 502 | return 0; |
493 | 503 | ||
494 | error: | 504 | error: |
495 | of_node_put(codec_np); | 505 | of_node_put(codec_np); |
496 | of_node_put(guts_np); | ||
497 | of_node_put(dma_np); | ||
498 | of_node_put(dma_channel_np); | ||
499 | 506 | ||
500 | if (sound_device) | 507 | if (sound_device) |
501 | platform_device_unregister(sound_device); | 508 | platform_device_unregister(sound_device); |
502 | 509 | ||
503 | if (machine_data->dai.cpu_dai) | ||
504 | fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); | ||
505 | |||
506 | if (ssi_info.ssi) | ||
507 | iounmap(ssi_info.ssi); | ||
508 | |||
509 | if (ssi_info.irq) | ||
510 | irq_dispose_mapping(ssi_info.irq); | ||
511 | |||
512 | if (dma_info.dma_channel[0]) | ||
513 | iounmap(dma_info.dma_channel[0]); | ||
514 | |||
515 | if (dma_info.dma_channel[1]) | ||
516 | iounmap(dma_info.dma_channel[1]); | ||
517 | |||
518 | if (dma_info.dma_irq[0]) | ||
519 | irq_dispose_mapping(dma_info.dma_irq[0]); | ||
520 | |||
521 | if (dma_info.dma_irq[1]) | ||
522 | irq_dispose_mapping(dma_info.dma_irq[1]); | ||
523 | |||
524 | if (machine_data->guts) | ||
525 | iounmap(machine_data->guts); | ||
526 | |||
527 | kfree(machine_data); | 510 | kfree(machine_data); |
528 | 511 | ||
529 | return ret; | 512 | return ret; |
530 | } | 513 | } |
531 | 514 | ||
532 | /** | 515 | /** |
533 | * mpc8610_hpcd_remove: remove the OF device | 516 | * mpc8610_hpcd_remove: remove the platform device |
534 | * | 517 | * |
535 | * This function is called when the OF device is removed. | 518 | * This function is called when the platform device is removed. |
536 | */ | 519 | */ |
537 | static int mpc8610_hpcd_remove(struct of_device *ofdev) | 520 | static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev) |
538 | { | 521 | { |
539 | struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); | 522 | struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); |
523 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
540 | struct mpc8610_hpcd_data *machine_data = | 524 | struct mpc8610_hpcd_data *machine_data = |
541 | sound_device->dev.platform_data; | 525 | container_of(card, struct mpc8610_hpcd_data, card); |
542 | 526 | ||
543 | platform_device_unregister(sound_device); | 527 | platform_device_unregister(sound_device); |
544 | 528 | ||
545 | if (machine_data->dai.cpu_dai) | ||
546 | fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); | ||
547 | |||
548 | if (machine_data->ssi) | ||
549 | iounmap(machine_data->ssi); | ||
550 | |||
551 | if (machine_data->dma[0]) | ||
552 | iounmap(machine_data->dma[0]); | ||
553 | |||
554 | if (machine_data->dma[1]) | ||
555 | iounmap(machine_data->dma[1]); | ||
556 | |||
557 | if (machine_data->dma_irq[0]) | ||
558 | irq_dispose_mapping(machine_data->dma_irq[0]); | ||
559 | |||
560 | if (machine_data->dma_irq[1]) | ||
561 | irq_dispose_mapping(machine_data->dma_irq[1]); | ||
562 | |||
563 | if (machine_data->guts) | ||
564 | iounmap(machine_data->guts); | ||
565 | |||
566 | kfree(machine_data); | 529 | kfree(machine_data); |
567 | sound_device->dev.platform_data = NULL; | 530 | sound_device->dev.platform_data = NULL; |
568 | 531 | ||
569 | dev_set_drvdata(&ofdev->dev, NULL); | 532 | dev_set_drvdata(&pdev->dev, NULL); |
570 | 533 | ||
571 | return 0; | 534 | return 0; |
572 | } | 535 | } |
573 | 536 | ||
574 | static struct of_device_id mpc8610_hpcd_match[] = { | 537 | static struct platform_driver mpc8610_hpcd_driver = { |
575 | { | 538 | .probe = mpc8610_hpcd_probe, |
576 | .compatible = "fsl,mpc8610-ssi", | 539 | .remove = __devexit_p(mpc8610_hpcd_remove), |
577 | }, | ||
578 | {} | ||
579 | }; | ||
580 | MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match); | ||
581 | |||
582 | static struct of_platform_driver mpc8610_hpcd_of_driver = { | ||
583 | .driver = { | 540 | .driver = { |
584 | .name = "mpc8610_hpcd", | 541 | /* The name must match the 'model' property in the device tree, |
542 | * in lowercase letters. | ||
543 | */ | ||
544 | .name = "snd-soc-mpc8610hpcd", | ||
585 | .owner = THIS_MODULE, | 545 | .owner = THIS_MODULE, |
586 | .of_match_table = mpc8610_hpcd_match, | ||
587 | }, | 546 | }, |
588 | .probe = mpc8610_hpcd_probe, | ||
589 | .remove = mpc8610_hpcd_remove, | ||
590 | }; | 547 | }; |
591 | 548 | ||
592 | /** | 549 | /** |
593 | * mpc8610_hpcd_init: fabric driver initialization. | 550 | * mpc8610_hpcd_init: machine driver initialization. |
594 | * | 551 | * |
595 | * This function is called when this module is loaded. | 552 | * This function is called when this module is loaded. |
596 | */ | 553 | */ |
597 | static int __init mpc8610_hpcd_init(void) | 554 | static int __init mpc8610_hpcd_init(void) |
598 | { | 555 | { |
599 | int ret; | 556 | struct device_node *guts_np; |
600 | 557 | struct resource res; | |
601 | printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); | ||
602 | 558 | ||
603 | ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); | 559 | pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); |
604 | 560 | ||
605 | if (ret) | 561 | /* Get the physical address of the global utilities registers */ |
606 | printk(KERN_ERR | 562 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); |
607 | "mpc8610-hpcd: failed to register platform driver\n"); | 563 | if (of_address_to_resource(guts_np, 0, &res)) { |
564 | pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); | ||
565 | return -EINVAL; | ||
566 | } | ||
567 | guts_phys = res.start; | ||
608 | 568 | ||
609 | return ret; | 569 | return platform_driver_register(&mpc8610_hpcd_driver); |
610 | } | 570 | } |
611 | 571 | ||
612 | /** | 572 | /** |
613 | * mpc8610_hpcd_exit: fabric driver exit | 573 | * mpc8610_hpcd_exit: machine driver exit |
614 | * | 574 | * |
615 | * This function is called when this driver is unloaded. | 575 | * This function is called when this driver is unloaded. |
616 | */ | 576 | */ |
617 | static void __exit mpc8610_hpcd_exit(void) | 577 | static void __exit mpc8610_hpcd_exit(void) |
618 | { | 578 | { |
619 | of_unregister_platform_driver(&mpc8610_hpcd_of_driver); | 579 | platform_driver_unregister(&mpc8610_hpcd_driver); |
620 | } | 580 | } |
621 | 581 | ||
622 | module_init(mpc8610_hpcd_init); | 582 | module_init(mpc8610_hpcd_init); |
623 | module_exit(mpc8610_hpcd_exit); | 583 | module_exit(mpc8610_hpcd_exit); |
624 | 584 | ||
625 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 585 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
626 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); | 586 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); |
627 | MODULE_LICENSE("GPL"); | 587 | MODULE_LICENSE("GPL v2"); |
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index 6644cba7cbf2..fe15bb26e484 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c | |||
@@ -32,21 +32,24 @@ | |||
32 | 32 | ||
33 | #define DRV_NAME "pcm030-audio-fabric" | 33 | #define DRV_NAME "pcm030-audio-fabric" |
34 | 34 | ||
35 | static struct snd_soc_device device; | ||
36 | static struct snd_soc_card card; | 35 | static struct snd_soc_card card; |
37 | 36 | ||
38 | static struct snd_soc_dai_link pcm030_fabric_dai[] = { | 37 | static struct snd_soc_dai_link pcm030_fabric_dai[] = { |
39 | { | 38 | { |
40 | .name = "AC97", | 39 | .name = "AC97", |
41 | .stream_name = "AC97 Analog", | 40 | .stream_name = "AC97 Analog", |
42 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], | 41 | .codec_dai_name = "wm9712-hifi", |
43 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL], | 42 | .cpu_dai_name = "mpc5200-psc-ac97.0", |
43 | .platform_name = "mpc5200-pcm-audio", | ||
44 | .codec_name = "wm9712-codec", | ||
44 | }, | 45 | }, |
45 | { | 46 | { |
46 | .name = "AC97", | 47 | .name = "AC97", |
47 | .stream_name = "AC97 IEC958", | 48 | .stream_name = "AC97 IEC958", |
48 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], | 49 | .codec_dai_name = "wm9712-aux", |
49 | .cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF], | 50 | .cpu_dai_name = "mpc5200-psc-ac97.1", |
51 | .platform_name = "mpc5200-pcm-audio", | ||
52 | ..codec_name = "wm9712-codec", | ||
50 | }, | 53 | }, |
51 | }; | 54 | }; |
52 | 55 | ||
@@ -58,22 +61,18 @@ static __init int pcm030_fabric_init(void) | |||
58 | if (!of_machine_is_compatible("phytec,pcm030")) | 61 | if (!of_machine_is_compatible("phytec,pcm030")) |
59 | return -ENODEV; | 62 | return -ENODEV; |
60 | 63 | ||
61 | card.platform = &mpc5200_audio_dma_platform; | 64 | |
62 | card.name = "pcm030"; | 65 | card.name = "pcm030"; |
63 | card.dai_link = pcm030_fabric_dai; | 66 | card.dai_link = pcm030_fabric_dai; |
64 | card.num_links = ARRAY_SIZE(pcm030_fabric_dai); | 67 | card.num_links = ARRAY_SIZE(pcm030_fabric_dai); |
65 | 68 | ||
66 | device.card = &card; | ||
67 | device.codec_dev = &soc_codec_dev_wm9712; | ||
68 | |||
69 | pdev = platform_device_alloc("soc-audio", 1); | 69 | pdev = platform_device_alloc("soc-audio", 1); |
70 | if (!pdev) { | 70 | if (!pdev) { |
71 | pr_err("pcm030_fabric_init: platform_device_alloc() failed\n"); | 71 | pr_err("pcm030_fabric_init: platform_device_alloc() failed\n"); |
72 | return -ENODEV; | 72 | return -ENODEV; |
73 | } | 73 | } |
74 | 74 | ||
75 | platform_set_drvdata(pdev, &device); | 75 | platform_set_drvdata(pdev, &card); |
76 | device.dev = &pdev->dev; | ||
77 | 76 | ||
78 | rc = platform_device_add(pdev); | 77 | rc = platform_device_add(pdev); |
79 | if (rc) { | 78 | if (rc) { |
diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c deleted file mode 100644 index 3bc13fd89096..000000000000 --- a/sound/soc/fsl/soc-of-simple.c +++ /dev/null | |||
@@ -1,172 +0,0 @@ | |||
1 | /* | ||
2 | * OF helpers for ALSA SoC Layer | ||
3 | * | ||
4 | * Copyright (C) 2008, Secret Lab Technologies Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <linux/module.h> | ||
8 | #include <linux/moduleparam.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/pm.h> | ||
12 | #include <linux/bitops.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <sound/core.h> | ||
17 | #include <sound/pcm.h> | ||
18 | #include <sound/pcm_params.h> | ||
19 | #include <sound/soc.h> | ||
20 | #include <sound/soc-of-simple.h> | ||
21 | #include <sound/initval.h> | ||
22 | |||
23 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); | ||
24 | MODULE_LICENSE("GPL"); | ||
25 | MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings"); | ||
26 | |||
27 | static DEFINE_MUTEX(of_snd_soc_mutex); | ||
28 | static LIST_HEAD(of_snd_soc_device_list); | ||
29 | static int of_snd_soc_next_index; | ||
30 | |||
31 | struct of_snd_soc_device { | ||
32 | int id; | ||
33 | struct list_head list; | ||
34 | struct snd_soc_device device; | ||
35 | struct snd_soc_card card; | ||
36 | struct snd_soc_dai_link dai_link; | ||
37 | struct platform_device *pdev; | ||
38 | struct device_node *platform_node; | ||
39 | struct device_node *codec_node; | ||
40 | }; | ||
41 | |||
42 | static struct snd_soc_ops of_snd_soc_ops = { | ||
43 | }; | ||
44 | |||
45 | static struct of_snd_soc_device * | ||
46 | of_snd_soc_get_device(struct device_node *codec_node) | ||
47 | { | ||
48 | struct of_snd_soc_device *of_soc; | ||
49 | |||
50 | list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { | ||
51 | if (of_soc->codec_node == codec_node) | ||
52 | return of_soc; | ||
53 | } | ||
54 | |||
55 | of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL); | ||
56 | if (!of_soc) | ||
57 | return NULL; | ||
58 | |||
59 | /* Initialize the structure and add it to the global list */ | ||
60 | of_soc->codec_node = codec_node; | ||
61 | of_soc->id = of_snd_soc_next_index++; | ||
62 | of_soc->card.dai_link = &of_soc->dai_link; | ||
63 | of_soc->card.num_links = 1; | ||
64 | of_soc->device.card = &of_soc->card; | ||
65 | of_soc->dai_link.ops = &of_snd_soc_ops; | ||
66 | list_add(&of_soc->list, &of_snd_soc_device_list); | ||
67 | |||
68 | return of_soc; | ||
69 | } | ||
70 | |||
71 | static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc) | ||
72 | { | ||
73 | struct platform_device *pdev; | ||
74 | int rc; | ||
75 | |||
76 | /* Only register the device if both the codec and platform have | ||
77 | * been registered */ | ||
78 | if ((!of_soc->device.codec_data) || (!of_soc->platform_node)) | ||
79 | return; | ||
80 | |||
81 | pr_info("platform<-->codec match achieved; registering machine\n"); | ||
82 | |||
83 | pdev = platform_device_alloc("soc-audio", of_soc->id); | ||
84 | if (!pdev) { | ||
85 | pr_err("of_soc: platform_device_alloc() failed\n"); | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | pdev->dev.platform_data = of_soc; | ||
90 | platform_set_drvdata(pdev, &of_soc->device); | ||
91 | of_soc->device.dev = &pdev->dev; | ||
92 | |||
93 | /* The ASoC device is complete; register it */ | ||
94 | rc = platform_device_add(pdev); | ||
95 | if (rc) { | ||
96 | pr_err("of_soc: platform_device_add() failed\n"); | ||
97 | return; | ||
98 | } | ||
99 | |||
100 | } | ||
101 | |||
102 | int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, | ||
103 | void *codec_data, struct snd_soc_dai *dai, | ||
104 | struct device_node *node) | ||
105 | { | ||
106 | struct of_snd_soc_device *of_soc; | ||
107 | int rc = 0; | ||
108 | |||
109 | pr_info("registering ASoC codec driver: %s\n", node->full_name); | ||
110 | |||
111 | mutex_lock(&of_snd_soc_mutex); | ||
112 | of_soc = of_snd_soc_get_device(node); | ||
113 | if (!of_soc) { | ||
114 | rc = -ENOMEM; | ||
115 | goto out; | ||
116 | } | ||
117 | |||
118 | /* Store the codec data */ | ||
119 | of_soc->device.codec_data = codec_data; | ||
120 | of_soc->device.codec_dev = codec_dev; | ||
121 | of_soc->dai_link.name = (char *)node->name; | ||
122 | of_soc->dai_link.stream_name = (char *)node->name; | ||
123 | of_soc->dai_link.codec_dai = dai; | ||
124 | |||
125 | /* Now try to register the SoC device */ | ||
126 | of_snd_soc_register_device(of_soc); | ||
127 | |||
128 | out: | ||
129 | mutex_unlock(&of_snd_soc_mutex); | ||
130 | return rc; | ||
131 | } | ||
132 | EXPORT_SYMBOL_GPL(of_snd_soc_register_codec); | ||
133 | |||
134 | int of_snd_soc_register_platform(struct snd_soc_platform *platform, | ||
135 | struct device_node *node, | ||
136 | struct snd_soc_dai *cpu_dai) | ||
137 | { | ||
138 | struct of_snd_soc_device *of_soc; | ||
139 | struct device_node *codec_node; | ||
140 | const phandle *handle; | ||
141 | int len, rc = 0; | ||
142 | |||
143 | pr_info("registering ASoC platform driver: %s\n", node->full_name); | ||
144 | |||
145 | handle = of_get_property(node, "codec-handle", &len); | ||
146 | if (!handle || len < sizeof(handle)) | ||
147 | return -ENODEV; | ||
148 | codec_node = of_find_node_by_phandle(*handle); | ||
149 | if (!codec_node) | ||
150 | return -ENODEV; | ||
151 | pr_info("looking for codec: %s\n", codec_node->full_name); | ||
152 | |||
153 | mutex_lock(&of_snd_soc_mutex); | ||
154 | of_soc = of_snd_soc_get_device(codec_node); | ||
155 | if (!of_soc) { | ||
156 | rc = -ENOMEM; | ||
157 | goto out; | ||
158 | } | ||
159 | |||
160 | of_soc->platform_node = node; | ||
161 | of_soc->dai_link.cpu_dai = cpu_dai; | ||
162 | of_soc->card.platform = platform; | ||
163 | of_soc->card.name = of_soc->dai_link.cpu_dai->name; | ||
164 | |||
165 | /* Now try to register the SoC device */ | ||
166 | of_snd_soc_register_device(of_soc); | ||
167 | |||
168 | out: | ||
169 | mutex_unlock(&of_snd_soc_mutex); | ||
170 | return rc; | ||
171 | } | ||
172 | EXPORT_SYMBOL_GPL(of_snd_soc_register_platform); | ||