diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-25 11:32:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-25 11:32:05 -0400 |
commit | 33081adf8b89d5a716d7e1c60171768d39795b39 (patch) | |
tree | 275de58bbbb5f7ddffcdc087844cfc7fbe4315be /sound/soc/fsl | |
parent | c55960499f810357a29659b32d6ea594abee9237 (diff) | |
parent | 506ecbca71d07fa327dd986be1682e90885678ee (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (365 commits)
ALSA: hda - Disable sticky PCM stream assignment for AD codecs
ALSA: usb - Creative USB X-Fi volume knob support
ALSA: ca0106: Use card specific dac id for mute controls.
ALSA: ca0106: Allow different sound cards to use different SPI channel mappings.
ALSA: ca0106: Create a nice spot for mapping channels to dacs.
ALSA: ca0106: Move enabling of front dac out of hardcoded setup sequence.
ALSA: ca0106: Pull out dac powering routine into separate function.
ALSA: ca0106 - add Sound Blaster 5.1vx info.
ASoC: tlv320dac33: Use usleep_range for delays
ALSA: usb-audio: add Novation Launchpad support
ALSA: hda - Add workarounds for CT-IBG controllers
ALSA: hda - Fix wrong TLV mute bit for STAC/IDT codecs
ASoC: tpa6130a2: Error handling for broken chip
ASoC: max98088: Staticise m98088_eq_band
ASoC: soc-core: Fix codec->name memory leak
ALSA: hda - Apply ideapad quirk to Acer laptops with Cxt5066
ALSA: hda - Add some workarounds for Creative IBG
ALSA: hda - Fix wrong SPDIF NID assignment for CA0110
ALSA: hda - Fix codec rename rules for ALC662-compatible codecs
ALSA: hda - Add alc_init_jacks() call to other codecs
...
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/Kconfig | 27 | ||||
-rw-r--r-- | sound/soc/fsl/Makefile | 11 | ||||
-rw-r--r-- | sound/soc/fsl/efika-audio-fabric.c | 20 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_dma.c | 458 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_dma.h | 20 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 298 | ||||
-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 | 660 | ||||
-rw-r--r-- | sound/soc/fsl/p1022_ds.c | 591 | ||||
-rw-r--r-- | sound/soc/fsl/pcm030-audio-fabric.c | 21 | ||||
-rw-r--r-- | sound/soc/fsl/soc-of-simple.c | 172 |
16 files changed, 1486 insertions, 944 deletions
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8cb65ccad35f..d754d34d68a6 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -1,27 +1,36 @@ | |||
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 | ||
7 | # ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers | 4 | # ASoC platform support for the Freescale PowerPC SOCs that have an SSI and |
8 | # for the SSI and the Elo DMA controller. You will still need to select | 5 | # an Elo DMA controller, such as the MPC8610 and P1022. You will still need to |
9 | # a platform driver and a codec driver. | 6 | # select a platform driver and a codec driver. |
10 | config SND_SOC_MPC8610 | 7 | config SND_SOC_POWERPC_SSI |
11 | tristate | 8 | tristate |
12 | depends on MPC8610 | 9 | depends on FSL_SOC |
13 | 10 | ||
14 | config SND_SOC_MPC8610_HPCD | 11 | config SND_SOC_MPC8610_HPCD |
15 | tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" | 12 | tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" |
16 | # I2C is necessary for the CS4270 driver | 13 | # I2C is necessary for the CS4270 driver |
17 | depends on MPC8610_HPCD && I2C | 14 | depends on MPC8610_HPCD && I2C |
18 | select SND_SOC_MPC8610 | 15 | select SND_SOC_POWERPC_SSI |
19 | select SND_SOC_CS4270 | 16 | select SND_SOC_CS4270 |
20 | select SND_SOC_CS4270_VD33_ERRATA | 17 | select SND_SOC_CS4270_VD33_ERRATA |
21 | default y if MPC8610_HPCD | 18 | default y if MPC8610_HPCD |
22 | help | 19 | help |
23 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. | 20 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. |
24 | 21 | ||
22 | config SND_SOC_P1022_DS | ||
23 | tristate "ALSA SoC support for the Freescale P1022 DS board" | ||
24 | # I2C is necessary for the WM8776 driver | ||
25 | depends on P1022_DS && I2C | ||
26 | select SND_SOC_POWERPC_SSI | ||
27 | select SND_SOC_WM8776 | ||
28 | default y if P1022_DS | ||
29 | help | ||
30 | Say Y if you want to enable audio on the Freescale P1022 DS board. | ||
31 | This will also include the Wolfson Microelectronics WM8776 codec | ||
32 | driver. | ||
33 | |||
25 | config SND_SOC_MPC5200_I2S | 34 | config SND_SOC_MPC5200_I2S |
26 | tristate "Freescale MPC5200 PSC in I2S mode driver" | 35 | tristate "Freescale MPC5200 PSC in I2S mode driver" |
27 | depends on PPC_MPC52xx && PPC_BESTCOMM | 36 | depends on PPC_MPC52xx && PPC_BESTCOMM |
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index a83a73967ec6..b4a38c0ac58c 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -1,14 +1,15 @@ | |||
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 |
7 | 4 | ||
8 | # MPC8610 Platform Support | 5 | # P1022 DS Machine Support |
6 | snd-soc-p1022-ds-objs := p1022_ds.o | ||
7 | obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o | ||
8 | |||
9 | # Freescale PowerPC SSI/DMA Platform Support | ||
9 | snd-soc-fsl-ssi-objs := fsl_ssi.o | 10 | snd-soc-fsl-ssi-objs := fsl_ssi.o |
10 | snd-soc-fsl-dma-objs := fsl_dma.o | 11 | snd-soc-fsl-dma-objs := fsl_dma.o |
11 | obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o | 12 | obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o |
12 | 13 | ||
13 | # MPC5200 Platform Support | 14 | # MPC5200 Platform Support |
14 | obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o | 15 | obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.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..4cf98c03af22 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,9 @@ | |||
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> | ||
26 | #include <linux/slab.h> | ||
23 | 27 | ||
24 | #include <sound/core.h> | 28 | #include <sound/core.h> |
25 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
@@ -29,6 +33,7 @@ | |||
29 | #include <asm/io.h> | 33 | #include <asm/io.h> |
30 | 34 | ||
31 | #include "fsl_dma.h" | 35 | #include "fsl_dma.h" |
36 | #include "fsl_ssi.h" /* For the offset of stx0 and srx0 */ | ||
32 | 37 | ||
33 | /* | 38 | /* |
34 | * The formats that the DMA controller supports, which is anything | 39 | * The formats that the DMA controller supports, which is anything |
@@ -52,26 +57,16 @@ | |||
52 | #define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ | 57 | #define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ |
53 | SNDRV_PCM_RATE_CONTINUOUS) | 58 | SNDRV_PCM_RATE_CONTINUOUS) |
54 | 59 | ||
55 | /* DMA global data. This structure is used by fsl_dma_open() to determine | 60 | struct dma_object { |
56 | * which DMA channels to assign to a substream. Unfortunately, ASoC V1 does | 61 | struct snd_soc_platform_driver dai; |
57 | * not allow the machine driver to provide this information to the PCM | ||
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 | unsigned int ssi_fifo_depth; |
72 | unsigned int irq[2]; | 65 | struct ccsr_dma_channel __iomem *channel; |
73 | unsigned int assigned[2]; | 66 | unsigned int irq; |
74 | } dma_global_data; | 67 | bool assigned; |
68 | char path[1]; | ||
69 | }; | ||
75 | 70 | ||
76 | /* | 71 | /* |
77 | * The number of DMA links to use. Two is the bare minimum, but if you | 72 | * The number of DMA links to use. Two is the bare minimum, but if you |
@@ -88,8 +83,6 @@ static struct { | |||
88 | * structure. | 83 | * structure. |
89 | * | 84 | * |
90 | * @link[]: array of link descriptors | 85 | * @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 | 86 | * @dma_channel: pointer to the DMA channel's registers |
94 | * @irq: IRQ for this DMA channel | 87 | * @irq: IRQ for this DMA channel |
95 | * @substream: pointer to the substream object, needed by the ISR | 88 | * @substream: pointer to the substream object, needed by the ISR |
@@ -104,12 +97,11 @@ static struct { | |||
104 | */ | 97 | */ |
105 | struct fsl_dma_private { | 98 | struct fsl_dma_private { |
106 | struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; | 99 | 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; | 100 | struct ccsr_dma_channel __iomem *dma_channel; |
110 | unsigned int irq; | 101 | unsigned int irq; |
111 | struct snd_pcm_substream *substream; | 102 | struct snd_pcm_substream *substream; |
112 | dma_addr_t ssi_sxx_phys; | 103 | dma_addr_t ssi_sxx_phys; |
104 | unsigned int ssi_fifo_depth; | ||
113 | dma_addr_t ld_buf_phys; | 105 | dma_addr_t ld_buf_phys; |
114 | unsigned int current_link; | 106 | unsigned int current_link; |
115 | dma_addr_t dma_buf_phys; | 107 | dma_addr_t dma_buf_phys; |
@@ -185,13 +177,23 @@ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) | |||
185 | struct fsl_dma_link_descriptor *link = | 177 | struct fsl_dma_link_descriptor *link = |
186 | &dma_private->link[dma_private->current_link]; | 178 | &dma_private->link[dma_private->current_link]; |
187 | 179 | ||
188 | /* Update our link descriptors to point to the next period */ | 180 | /* Update our link descriptors to point to the next period. On a 36-bit |
189 | if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 181 | * system, we also need to update the ESAD bits. We also set (keep) the |
190 | link->source_addr = | 182 | * snoop bits. See the comments in fsl_dma_hw_params() about snooping. |
191 | cpu_to_be32(dma_private->dma_buf_next); | 183 | */ |
192 | else | 184 | if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
193 | link->dest_addr = | 185 | link->source_addr = cpu_to_be32(dma_private->dma_buf_next); |
194 | cpu_to_be32(dma_private->dma_buf_next); | 186 | #ifdef CONFIG_PHYS_64BIT |
187 | link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | | ||
188 | upper_32_bits(dma_private->dma_buf_next)); | ||
189 | #endif | ||
190 | } else { | ||
191 | link->dest_addr = cpu_to_be32(dma_private->dma_buf_next); | ||
192 | #ifdef CONFIG_PHYS_64BIT | ||
193 | link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | | ||
194 | upper_32_bits(dma_private->dma_buf_next)); | ||
195 | #endif | ||
196 | } | ||
195 | 197 | ||
196 | /* Update our variables for next time */ | 198 | /* Update our variables for next time */ |
197 | dma_private->dma_buf_next += dma_private->period_size; | 199 | dma_private->dma_buf_next += dma_private->period_size; |
@@ -212,6 +214,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) | 214 | static irqreturn_t fsl_dma_isr(int irq, void *dev_id) |
213 | { | 215 | { |
214 | struct fsl_dma_private *dma_private = dev_id; | 216 | struct fsl_dma_private *dma_private = dev_id; |
217 | struct snd_pcm_substream *substream = dma_private->substream; | ||
218 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
219 | struct device *dev = rtd->platform->dev; | ||
215 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | 220 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; |
216 | irqreturn_t ret = IRQ_NONE; | 221 | irqreturn_t ret = IRQ_NONE; |
217 | u32 sr, sr2 = 0; | 222 | u32 sr, sr2 = 0; |
@@ -222,11 +227,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
222 | sr = in_be32(&dma_channel->sr); | 227 | sr = in_be32(&dma_channel->sr); |
223 | 228 | ||
224 | if (sr & CCSR_DMA_SR_TE) { | 229 | if (sr & CCSR_DMA_SR_TE) { |
225 | dev_err(dma_private->substream->pcm->card->dev, | 230 | dev_err(dev, "dma transmit error\n"); |
226 | "DMA transmit error (controller=%u channel=%u irq=%u\n", | 231 | 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; | 232 | sr2 |= CCSR_DMA_SR_TE; |
231 | ret = IRQ_HANDLED; | 233 | ret = IRQ_HANDLED; |
232 | } | 234 | } |
@@ -235,11 +237,8 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
235 | ret = IRQ_HANDLED; | 237 | ret = IRQ_HANDLED; |
236 | 238 | ||
237 | if (sr & CCSR_DMA_SR_PE) { | 239 | if (sr & CCSR_DMA_SR_PE) { |
238 | dev_err(dma_private->substream->pcm->card->dev, | 240 | dev_err(dev, "dma programming error\n"); |
239 | "DMA%u programming error (channel=%u irq=%u)\n", | 241 | 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; | 242 | sr2 |= CCSR_DMA_SR_PE; |
244 | ret = IRQ_HANDLED; | 243 | ret = IRQ_HANDLED; |
245 | } | 244 | } |
@@ -253,8 +252,6 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
253 | ret = IRQ_HANDLED; | 252 | ret = IRQ_HANDLED; |
254 | 253 | ||
255 | if (sr & CCSR_DMA_SR_EOSI) { | 254 | if (sr & CCSR_DMA_SR_EOSI) { |
256 | struct snd_pcm_substream *substream = dma_private->substream; | ||
257 | |||
258 | /* Tell ALSA we completed a period. */ | 255 | /* Tell ALSA we completed a period. */ |
259 | snd_pcm_period_elapsed(substream); | 256 | snd_pcm_period_elapsed(substream); |
260 | 257 | ||
@@ -288,11 +285,19 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | |||
288 | * This function is called when the codec driver calls snd_soc_new_pcms(), | 285 | * This function is called when the codec driver calls snd_soc_new_pcms(), |
289 | * once for each .dai_link in the machine driver's snd_soc_card | 286 | * once for each .dai_link in the machine driver's snd_soc_card |
290 | * structure. | 287 | * structure. |
288 | * | ||
289 | * snd_dma_alloc_pages() is just a front-end to dma_alloc_coherent(), which | ||
290 | * (currently) always allocates the DMA buffer in lowmem, even if GFP_HIGHMEM | ||
291 | * is specified. Therefore, any DMA buffers we allocate will always be in low | ||
292 | * memory, but we support for 36-bit physical addresses anyway. | ||
293 | * | ||
294 | * Regardless of where the memory is actually allocated, since the device can | ||
295 | * technically DMA to any 36-bit address, we do need to set the DMA mask to 36. | ||
291 | */ | 296 | */ |
292 | static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | 297 | static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, |
293 | struct snd_pcm *pcm) | 298 | struct snd_pcm *pcm) |
294 | { | 299 | { |
295 | static u64 fsl_dma_dmamask = DMA_BIT_MASK(32); | 300 | static u64 fsl_dma_dmamask = DMA_BIT_MASK(36); |
296 | int ret; | 301 | int ret; |
297 | 302 | ||
298 | if (!card->dev->dma_mask) | 303 | if (!card->dev->dma_mask) |
@@ -301,25 +306,29 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
301 | if (!card->dev->coherent_dma_mask) | 306 | if (!card->dev->coherent_dma_mask) |
302 | card->dev->coherent_dma_mask = fsl_dma_dmamask; | 307 | card->dev->coherent_dma_mask = fsl_dma_dmamask; |
303 | 308 | ||
304 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, | 309 | /* Some codecs have separate DAIs for playback and capture, so we |
305 | fsl_dma_hardware.buffer_bytes_max, | 310 | * should allocate a DMA buffer only for the streams that are valid. |
306 | &pcm->streams[0].substream->dma_buffer); | 311 | */ |
307 | if (ret) { | 312 | |
308 | dev_err(card->dev, | 313 | if (dai->driver->playback.channels_min) { |
309 | "Can't allocate playback DMA buffer (size=%u)\n", | 314 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, |
310 | fsl_dma_hardware.buffer_bytes_max); | 315 | fsl_dma_hardware.buffer_bytes_max, |
311 | return -ENOMEM; | 316 | &pcm->streams[0].substream->dma_buffer); |
317 | if (ret) { | ||
318 | dev_err(card->dev, "can't alloc playback dma buffer\n"); | ||
319 | return ret; | ||
320 | } | ||
312 | } | 321 | } |
313 | 322 | ||
314 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, | 323 | if (dai->driver->capture.channels_min) { |
315 | fsl_dma_hardware.buffer_bytes_max, | 324 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, |
316 | &pcm->streams[1].substream->dma_buffer); | 325 | fsl_dma_hardware.buffer_bytes_max, |
317 | if (ret) { | 326 | &pcm->streams[1].substream->dma_buffer); |
318 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | 327 | if (ret) { |
319 | dev_err(card->dev, | 328 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); |
320 | "Can't allocate capture DMA buffer (size=%u)\n", | 329 | dev_err(card->dev, "can't alloc capture dma buffer\n"); |
321 | fsl_dma_hardware.buffer_bytes_max); | 330 | return ret; |
322 | return -ENOMEM; | 331 | } |
323 | } | 332 | } |
324 | 333 | ||
325 | return 0; | 334 | return 0; |
@@ -390,6 +399,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) | 399 | static int fsl_dma_open(struct snd_pcm_substream *substream) |
391 | { | 400 | { |
392 | struct snd_pcm_runtime *runtime = substream->runtime; | 401 | struct snd_pcm_runtime *runtime = substream->runtime; |
402 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
403 | struct device *dev = rtd->platform->dev; | ||
404 | struct dma_object *dma = | ||
405 | container_of(rtd->platform->driver, struct dma_object, dai); | ||
393 | struct fsl_dma_private *dma_private; | 406 | struct fsl_dma_private *dma_private; |
394 | struct ccsr_dma_channel __iomem *dma_channel; | 407 | struct ccsr_dma_channel __iomem *dma_channel; |
395 | dma_addr_t ld_buf_phys; | 408 | dma_addr_t ld_buf_phys; |
@@ -407,52 +420,45 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) | |||
407 | ret = snd_pcm_hw_constraint_integer(runtime, | 420 | ret = snd_pcm_hw_constraint_integer(runtime, |
408 | SNDRV_PCM_HW_PARAM_PERIODS); | 421 | SNDRV_PCM_HW_PARAM_PERIODS); |
409 | if (ret < 0) { | 422 | if (ret < 0) { |
410 | dev_err(substream->pcm->card->dev, "invalid buffer size\n"); | 423 | dev_err(dev, "invalid buffer size\n"); |
411 | return ret; | 424 | return ret; |
412 | } | 425 | } |
413 | 426 | ||
414 | channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | 427 | channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; |
415 | 428 | ||
416 | if (dma_global_data.assigned[channel]) { | 429 | if (dma->assigned) { |
417 | dev_err(substream->pcm->card->dev, | 430 | dev_err(dev, "dma channel already assigned\n"); |
418 | "DMA channel already assigned\n"); | ||
419 | return -EBUSY; | 431 | return -EBUSY; |
420 | } | 432 | } |
421 | 433 | ||
422 | dma_private = dma_alloc_coherent(substream->pcm->card->dev, | 434 | dma_private = dma_alloc_coherent(dev, sizeof(struct fsl_dma_private), |
423 | sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL); | 435 | &ld_buf_phys, GFP_KERNEL); |
424 | if (!dma_private) { | 436 | if (!dma_private) { |
425 | dev_err(substream->pcm->card->dev, | 437 | dev_err(dev, "can't allocate dma private data\n"); |
426 | "can't allocate DMA private data\n"); | ||
427 | return -ENOMEM; | 438 | return -ENOMEM; |
428 | } | 439 | } |
429 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 440 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
430 | dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys; | 441 | dma_private->ssi_sxx_phys = dma->ssi_stx_phys; |
431 | else | 442 | else |
432 | dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys; | 443 | dma_private->ssi_sxx_phys = dma->ssi_srx_phys; |
433 | 444 | ||
434 | dma_private->dma_channel = dma_global_data.dma_channel[channel]; | 445 | dma_private->ssi_fifo_depth = dma->ssi_fifo_depth; |
435 | dma_private->irq = dma_global_data.irq[channel]; | 446 | dma_private->dma_channel = dma->channel; |
447 | dma_private->irq = dma->irq; | ||
436 | dma_private->substream = substream; | 448 | dma_private->substream = substream; |
437 | dma_private->ld_buf_phys = ld_buf_phys; | 449 | dma_private->ld_buf_phys = ld_buf_phys; |
438 | dma_private->dma_buf_phys = substream->dma_buffer.addr; | 450 | dma_private->dma_buf_phys = substream->dma_buffer.addr; |
439 | 451 | ||
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); | 452 | ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private); |
445 | if (ret) { | 453 | if (ret) { |
446 | dev_err(substream->pcm->card->dev, | 454 | 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); | 455 | dma_private->irq, ret); |
449 | dma_free_coherent(substream->pcm->card->dev, | 456 | dma_free_coherent(dev, sizeof(struct fsl_dma_private), |
450 | sizeof(struct fsl_dma_private), | ||
451 | dma_private, dma_private->ld_buf_phys); | 457 | dma_private, dma_private->ld_buf_phys); |
452 | return ret; | 458 | return ret; |
453 | } | 459 | } |
454 | 460 | ||
455 | dma_global_data.assigned[channel] = 1; | 461 | dma->assigned = 1; |
456 | 462 | ||
457 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 463 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
458 | snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); | 464 | snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); |
@@ -546,13 +552,15 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
546 | { | 552 | { |
547 | struct snd_pcm_runtime *runtime = substream->runtime; | 553 | struct snd_pcm_runtime *runtime = substream->runtime; |
548 | struct fsl_dma_private *dma_private = runtime->private_data; | 554 | struct fsl_dma_private *dma_private = runtime->private_data; |
555 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
556 | struct device *dev = rtd->platform->dev; | ||
549 | 557 | ||
550 | /* Number of bits per sample */ | 558 | /* Number of bits per sample */ |
551 | unsigned int sample_size = | 559 | unsigned int sample_bits = |
552 | snd_pcm_format_physical_width(params_format(hw_params)); | 560 | snd_pcm_format_physical_width(params_format(hw_params)); |
553 | 561 | ||
554 | /* Number of bytes per frame */ | 562 | /* Number of bytes per frame */ |
555 | unsigned int frame_size = 2 * (sample_size / 8); | 563 | unsigned int sample_bytes = sample_bits / 8; |
556 | 564 | ||
557 | /* Bus address of SSI STX register */ | 565 | /* Bus address of SSI STX register */ |
558 | dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys; | 566 | dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys; |
@@ -592,7 +600,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
592 | * that offset here. While we're at it, also tell the DMA controller | 600 | * that offset here. While we're at it, also tell the DMA controller |
593 | * how much data to transfer per sample. | 601 | * how much data to transfer per sample. |
594 | */ | 602 | */ |
595 | switch (sample_size) { | 603 | switch (sample_bits) { |
596 | case 8: | 604 | case 8: |
597 | mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; | 605 | mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; |
598 | ssi_sxx_phys += 3; | 606 | ssi_sxx_phys += 3; |
@@ -606,23 +614,42 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
606 | break; | 614 | break; |
607 | default: | 615 | default: |
608 | /* We should never get here */ | 616 | /* We should never get here */ |
609 | dev_err(substream->pcm->card->dev, | 617 | dev_err(dev, "unsupported sample size %u\n", sample_bits); |
610 | "unsupported sample size %u\n", sample_size); | ||
611 | return -EINVAL; | 618 | return -EINVAL; |
612 | } | 619 | } |
613 | 620 | ||
614 | /* | 621 | /* |
615 | * BWC should always be a multiple of the frame size. BWC determines | 622 | * BWC determines how many bytes are sent/received before the DMA |
616 | * how many bytes are sent/received before the DMA controller checks the | 623 | * controller checks the SSI to see if it needs to stop. BWC should |
617 | * SSI to see if it needs to stop. For playback, the transmit FIFO can | 624 | * always be a multiple of the frame size, so that we always transmit |
618 | * hold three frames, so we want to send two frames at a time. For | 625 | * whole frames. Each frame occupies two slots in the FIFO. The |
619 | * capture, the receive FIFO is triggered when it contains one frame, so | 626 | * parameter for CCSR_DMA_MR_BWC() is rounded down the next power of two |
620 | * we want to receive one frame at a time. | 627 | * (MR[BWC] can only represent even powers of two). |
628 | * | ||
629 | * To simplify the process, we set BWC to the largest value that is | ||
630 | * less than or equal to the FIFO watermark. For playback, this ensures | ||
631 | * that we transfer the maximum amount without overrunning the FIFO. | ||
632 | * For capture, this ensures that we transfer the maximum amount without | ||
633 | * underrunning the FIFO. | ||
634 | * | ||
635 | * f = SSI FIFO depth | ||
636 | * w = SSI watermark value (which equals f - 2) | ||
637 | * b = DMA bandwidth count (in bytes) | ||
638 | * s = sample size (in bytes, which equals frame_size * 2) | ||
639 | * | ||
640 | * For playback, we never transmit more than the transmit FIFO | ||
641 | * watermark, otherwise we might write more data than the FIFO can hold. | ||
642 | * The watermark is equal to the FIFO depth minus two. | ||
643 | * | ||
644 | * For capture, two equations must hold: | ||
645 | * w > f - (b / s) | ||
646 | * w >= b / s | ||
647 | * | ||
648 | * So, b > 2 * s, but b must also be <= s * w. To simplify, we set | ||
649 | * b = s * w, which is equal to | ||
650 | * (dma_private->ssi_fifo_depth - 2) * sample_bytes. | ||
621 | */ | 651 | */ |
622 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 652 | mr |= CCSR_DMA_MR_BWC((dma_private->ssi_fifo_depth - 2) * sample_bytes); |
623 | mr |= CCSR_DMA_MR_BWC(2 * frame_size); | ||
624 | else | ||
625 | mr |= CCSR_DMA_MR_BWC(frame_size); | ||
626 | 653 | ||
627 | out_be32(&dma_channel->mr, mr); | 654 | out_be32(&dma_channel->mr, mr); |
628 | 655 | ||
@@ -631,12 +658,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
631 | 658 | ||
632 | link->count = cpu_to_be32(period_size); | 659 | link->count = cpu_to_be32(period_size); |
633 | 660 | ||
634 | /* Even though the DMA controller supports 36-bit addressing, | 661 | /* The snoop bit tells the DMA controller whether it should tell |
635 | * for simplicity we allow only 32-bit addresses for the audio | ||
636 | * buffer itself. This was enforced in fsl_dma_new() with the | ||
637 | * DMA mask. | ||
638 | * | ||
639 | * The snoop bit tells the DMA controller whether it should tell | ||
640 | * the ECM to snoop during a read or write to an address. For | 662 | * the ECM to snoop during a read or write to an address. For |
641 | * audio, we use DMA to transfer data between memory and an I/O | 663 | * audio, we use DMA to transfer data between memory and an I/O |
642 | * device (the SSI's STX0 or SRX0 register). Snooping is only | 664 | * device (the SSI's STX0 or SRX0 register). Snooping is only |
@@ -651,20 +673,24 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
651 | * flush out the data for the previous period. So if you | 673 | * flush out the data for the previous period. So if you |
652 | * increased period_bytes_min to a large enough size, you might | 674 | * increased period_bytes_min to a large enough size, you might |
653 | * get more performance by not snooping, and you'll still be | 675 | * get more performance by not snooping, and you'll still be |
654 | * okay. | 676 | * okay. You'll need to update fsl_dma_update_pointers() also. |
655 | */ | 677 | */ |
656 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 678 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
657 | link->source_addr = cpu_to_be32(temp_addr); | 679 | link->source_addr = cpu_to_be32(temp_addr); |
658 | link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); | 680 | link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | |
681 | upper_32_bits(temp_addr)); | ||
659 | 682 | ||
660 | link->dest_addr = cpu_to_be32(ssi_sxx_phys); | 683 | link->dest_addr = cpu_to_be32(ssi_sxx_phys); |
661 | link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP); | 684 | link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP | |
685 | upper_32_bits(ssi_sxx_phys)); | ||
662 | } else { | 686 | } else { |
663 | link->source_addr = cpu_to_be32(ssi_sxx_phys); | 687 | link->source_addr = cpu_to_be32(ssi_sxx_phys); |
664 | link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP); | 688 | link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP | |
689 | upper_32_bits(ssi_sxx_phys)); | ||
665 | 690 | ||
666 | link->dest_addr = cpu_to_be32(temp_addr); | 691 | link->dest_addr = cpu_to_be32(temp_addr); |
667 | link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); | 692 | link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP | |
693 | upper_32_bits(temp_addr)); | ||
668 | } | 694 | } |
669 | 695 | ||
670 | temp_addr += period_size; | 696 | temp_addr += period_size; |
@@ -689,14 +715,29 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) | |||
689 | { | 715 | { |
690 | struct snd_pcm_runtime *runtime = substream->runtime; | 716 | struct snd_pcm_runtime *runtime = substream->runtime; |
691 | struct fsl_dma_private *dma_private = runtime->private_data; | 717 | struct fsl_dma_private *dma_private = runtime->private_data; |
718 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
719 | struct device *dev = rtd->platform->dev; | ||
692 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | 720 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; |
693 | dma_addr_t position; | 721 | dma_addr_t position; |
694 | snd_pcm_uframes_t frames; | 722 | snd_pcm_uframes_t frames; |
695 | 723 | ||
696 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 724 | /* Obtain the current DMA pointer, but don't read the ESAD bits if we |
725 | * only have 32-bit DMA addresses. This function is typically called | ||
726 | * in interrupt context, so we need to optimize it. | ||
727 | */ | ||
728 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
697 | position = in_be32(&dma_channel->sar); | 729 | position = in_be32(&dma_channel->sar); |
698 | else | 730 | #ifdef CONFIG_PHYS_64BIT |
731 | position |= (u64)(in_be32(&dma_channel->satr) & | ||
732 | CCSR_DMA_ATR_ESAD_MASK) << 32; | ||
733 | #endif | ||
734 | } else { | ||
699 | position = in_be32(&dma_channel->dar); | 735 | position = in_be32(&dma_channel->dar); |
736 | #ifdef CONFIG_PHYS_64BIT | ||
737 | position |= (u64)(in_be32(&dma_channel->datr) & | ||
738 | CCSR_DMA_ATR_ESAD_MASK) << 32; | ||
739 | #endif | ||
740 | } | ||
700 | 741 | ||
701 | /* | 742 | /* |
702 | * When capture is started, the SSI immediately starts to fill its FIFO. | 743 | * When capture is started, the SSI immediately starts to fill its FIFO. |
@@ -710,8 +751,7 @@ static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) | |||
710 | 751 | ||
711 | if ((position < dma_private->dma_buf_phys) || | 752 | if ((position < dma_private->dma_buf_phys) || |
712 | (position > dma_private->dma_buf_end)) { | 753 | (position > dma_private->dma_buf_end)) { |
713 | dev_err(substream->pcm->card->dev, | 754 | 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; | 755 | return SNDRV_PCM_POS_XRUN; |
716 | } | 756 | } |
717 | 757 | ||
@@ -772,26 +812,28 @@ static int fsl_dma_close(struct snd_pcm_substream *substream) | |||
772 | { | 812 | { |
773 | struct snd_pcm_runtime *runtime = substream->runtime; | 813 | struct snd_pcm_runtime *runtime = substream->runtime; |
774 | struct fsl_dma_private *dma_private = runtime->private_data; | 814 | struct fsl_dma_private *dma_private = runtime->private_data; |
775 | int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | 815 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
816 | struct device *dev = rtd->platform->dev; | ||
817 | struct dma_object *dma = | ||
818 | container_of(rtd->platform->driver, struct dma_object, dai); | ||
776 | 819 | ||
777 | if (dma_private) { | 820 | if (dma_private) { |
778 | if (dma_private->irq) | 821 | if (dma_private->irq) |
779 | free_irq(dma_private->irq, dma_private); | 822 | free_irq(dma_private->irq, dma_private); |
780 | 823 | ||
781 | if (dma_private->ld_buf_phys) { | 824 | if (dma_private->ld_buf_phys) { |
782 | dma_unmap_single(substream->pcm->card->dev, | 825 | dma_unmap_single(dev, dma_private->ld_buf_phys, |
783 | dma_private->ld_buf_phys, | 826 | sizeof(dma_private->link), |
784 | sizeof(dma_private->link), DMA_TO_DEVICE); | 827 | DMA_TO_DEVICE); |
785 | } | 828 | } |
786 | 829 | ||
787 | /* Deallocate the fsl_dma_private structure */ | 830 | /* Deallocate the fsl_dma_private structure */ |
788 | dma_free_coherent(substream->pcm->card->dev, | 831 | dma_free_coherent(dev, sizeof(struct fsl_dma_private), |
789 | sizeof(struct fsl_dma_private), | 832 | dma_private, dma_private->ld_buf_phys); |
790 | dma_private, dma_private->ld_buf_phys); | ||
791 | substream->runtime->private_data = NULL; | 833 | substream->runtime->private_data = NULL; |
792 | } | 834 | } |
793 | 835 | ||
794 | dma_global_data.assigned[dir] = 0; | 836 | dma->assigned = 0; |
795 | 837 | ||
796 | return 0; | 838 | return 0; |
797 | } | 839 | } |
@@ -814,6 +856,37 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) | |||
814 | } | 856 | } |
815 | } | 857 | } |
816 | 858 | ||
859 | /** | ||
860 | * find_ssi_node -- returns the SSI node that points to his DMA channel node | ||
861 | * | ||
862 | * Although this DMA driver attempts to operate independently of the other | ||
863 | * devices, it still needs to determine some information about the SSI device | ||
864 | * that it's working with. Unfortunately, the device tree does not contain | ||
865 | * a pointer from the DMA channel node to the SSI node -- the pointer goes the | ||
866 | * other way. So we need to scan the device tree for SSI nodes until we find | ||
867 | * the one that points to the given DMA channel node. It's ugly, but at least | ||
868 | * it's contained in this one function. | ||
869 | */ | ||
870 | static struct device_node *find_ssi_node(struct device_node *dma_channel_np) | ||
871 | { | ||
872 | struct device_node *ssi_np, *np; | ||
873 | |||
874 | for_each_compatible_node(ssi_np, NULL, "fsl,mpc8610-ssi") { | ||
875 | /* Check each DMA phandle to see if it points to us. We | ||
876 | * assume that device_node pointers are a valid comparison. | ||
877 | */ | ||
878 | np = of_parse_phandle(ssi_np, "fsl,playback-dma", 0); | ||
879 | if (np == dma_channel_np) | ||
880 | return ssi_np; | ||
881 | |||
882 | np = of_parse_phandle(ssi_np, "fsl,capture-dma", 0); | ||
883 | if (np == dma_channel_np) | ||
884 | return ssi_np; | ||
885 | } | ||
886 | |||
887 | return NULL; | ||
888 | } | ||
889 | |||
817 | static struct snd_pcm_ops fsl_dma_ops = { | 890 | static struct snd_pcm_ops fsl_dma_ops = { |
818 | .open = fsl_dma_open, | 891 | .open = fsl_dma_open, |
819 | .close = fsl_dma_close, | 892 | .close = fsl_dma_close, |
@@ -823,59 +896,114 @@ static struct snd_pcm_ops fsl_dma_ops = { | |||
823 | .pointer = fsl_dma_pointer, | 896 | .pointer = fsl_dma_pointer, |
824 | }; | 897 | }; |
825 | 898 | ||
826 | struct snd_soc_platform fsl_soc_platform = { | 899 | static int __devinit fsl_soc_dma_probe(struct platform_device *pdev, |
827 | .name = "fsl-dma", | 900 | const struct of_device_id *match) |
828 | .pcm_ops = &fsl_dma_ops, | 901 | { |
829 | .pcm_new = fsl_dma_new, | 902 | struct dma_object *dma; |
830 | .pcm_free = fsl_dma_free_dma_buffers, | 903 | struct device_node *np = pdev->dev.of_node; |
831 | }; | 904 | struct device_node *ssi_np; |
832 | EXPORT_SYMBOL_GPL(fsl_soc_platform); | 905 | struct resource res; |
906 | const uint32_t *iprop; | ||
907 | int ret; | ||
833 | 908 | ||
834 | /** | 909 | /* Find the SSI node that points to us. */ |
835 | * fsl_dma_configure: store the DMA parameters from the fabric driver. | 910 | ssi_np = find_ssi_node(np); |
836 | * | 911 | if (!ssi_np) { |
837 | * This function is called by the ASoC fabric driver to give us the DMA and | 912 | dev_err(&pdev->dev, "cannot find parent SSI node\n"); |
838 | * SSI channel information. | 913 | return -ENODEV; |
839 | * | 914 | } |
840 | * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI | 915 | |
841 | * data when a substream is created, so for now we need to store this data | 916 | ret = of_address_to_resource(ssi_np, 0, &res); |
842 | * into a global variable. This means that we can only support one DMA | 917 | if (ret) { |
843 | * controller, and hence only one SSI. | 918 | dev_err(&pdev->dev, "could not determine resources for %s\n", |
844 | */ | 919 | ssi_np->full_name); |
845 | int fsl_dma_configure(struct fsl_dma_info *dma_info) | 920 | of_node_put(ssi_np); |
921 | return ret; | ||
922 | } | ||
923 | |||
924 | dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); | ||
925 | if (!dma) { | ||
926 | dev_err(&pdev->dev, "could not allocate dma object\n"); | ||
927 | of_node_put(ssi_np); | ||
928 | return -ENOMEM; | ||
929 | } | ||
930 | |||
931 | strcpy(dma->path, np->full_name); | ||
932 | dma->dai.ops = &fsl_dma_ops; | ||
933 | dma->dai.pcm_new = fsl_dma_new; | ||
934 | dma->dai.pcm_free = fsl_dma_free_dma_buffers; | ||
935 | |||
936 | /* Store the SSI-specific information that we need */ | ||
937 | dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); | ||
938 | dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); | ||
939 | |||
940 | iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); | ||
941 | if (iprop) | ||
942 | dma->ssi_fifo_depth = *iprop; | ||
943 | else | ||
944 | /* Older 8610 DTs didn't have the fifo-depth property */ | ||
945 | dma->ssi_fifo_depth = 8; | ||
946 | |||
947 | of_node_put(ssi_np); | ||
948 | |||
949 | ret = snd_soc_register_platform(&pdev->dev, &dma->dai); | ||
950 | if (ret) { | ||
951 | dev_err(&pdev->dev, "could not register platform\n"); | ||
952 | kfree(dma); | ||
953 | return ret; | ||
954 | } | ||
955 | |||
956 | dma->channel = of_iomap(np, 0); | ||
957 | dma->irq = irq_of_parse_and_map(np, 0); | ||
958 | |||
959 | dev_set_drvdata(&pdev->dev, dma); | ||
960 | |||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static int __devexit fsl_soc_dma_remove(struct platform_device *pdev) | ||
846 | { | 965 | { |
847 | static int initialized; | 966 | struct dma_object *dma = dev_get_drvdata(&pdev->dev); |
848 | 967 | ||
849 | /* We only support one DMA controller for now */ | 968 | snd_soc_unregister_platform(&pdev->dev); |
850 | if (initialized) | 969 | iounmap(dma->channel); |
851 | return 0; | 970 | irq_dispose_mapping(dma->irq); |
971 | kfree(dma); | ||
852 | 972 | ||
853 | dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys; | 973 | 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 | } | 974 | } |
865 | EXPORT_SYMBOL_GPL(fsl_dma_configure); | ||
866 | 975 | ||
867 | static int __init fsl_soc_platform_init(void) | 976 | static const struct of_device_id fsl_soc_dma_ids[] = { |
977 | { .compatible = "fsl,ssi-dma-channel", }, | ||
978 | {} | ||
979 | }; | ||
980 | MODULE_DEVICE_TABLE(of, fsl_soc_dma_ids); | ||
981 | |||
982 | static struct of_platform_driver fsl_soc_dma_driver = { | ||
983 | .driver = { | ||
984 | .name = "fsl-pcm-audio", | ||
985 | .owner = THIS_MODULE, | ||
986 | .of_match_table = fsl_soc_dma_ids, | ||
987 | }, | ||
988 | .probe = fsl_soc_dma_probe, | ||
989 | .remove = __devexit_p(fsl_soc_dma_remove), | ||
990 | }; | ||
991 | |||
992 | static int __init fsl_soc_dma_init(void) | ||
868 | { | 993 | { |
869 | return snd_soc_register_platform(&fsl_soc_platform); | 994 | pr_info("Freescale Elo DMA ASoC PCM Driver\n"); |
995 | |||
996 | return of_register_platform_driver(&fsl_soc_dma_driver); | ||
870 | } | 997 | } |
871 | module_init(fsl_soc_platform_init); | ||
872 | 998 | ||
873 | static void __exit fsl_soc_platform_exit(void) | 999 | static void __exit fsl_soc_dma_exit(void) |
874 | { | 1000 | { |
875 | snd_soc_unregister_platform(&fsl_soc_platform); | 1001 | of_unregister_platform_driver(&fsl_soc_dma_driver); |
876 | } | 1002 | } |
877 | module_exit(fsl_soc_platform_exit); | 1003 | |
1004 | module_init(fsl_soc_dma_init); | ||
1005 | module_exit(fsl_soc_dma_exit); | ||
878 | 1006 | ||
879 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 1007 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
880 | MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module"); | 1008 | MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM Driver"); |
881 | MODULE_LICENSE("GPL"); | 1009 | 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..4cc167a7aeb8 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> |
@@ -22,8 +24,6 @@ | |||
22 | #include <sound/initval.h> | 24 | #include <sound/initval.h> |
23 | #include <sound/soc.h> | 25 | #include <sound/soc.h> |
24 | 26 | ||
25 | #include <asm/immap_86xx.h> | ||
26 | |||
27 | #include "fsl_ssi.h" | 27 | #include "fsl_ssi.h" |
28 | 28 | ||
29 | /** | 29 | /** |
@@ -71,33 +71,32 @@ | |||
71 | /** | 71 | /** |
72 | * fsl_ssi_private: per-SSI private data | 72 | * fsl_ssi_private: per-SSI private data |
73 | * | 73 | * |
74 | * @name: short name for this device ("SSI0", "SSI1", etc) | ||
75 | * @ssi: pointer to the SSI's registers | 74 | * @ssi: pointer to the SSI's registers |
76 | * @ssi_phys: physical address of the SSI registers | 75 | * @ssi_phys: physical address of the SSI registers |
77 | * @irq: IRQ of this SSI | 76 | * @irq: IRQ of this SSI |
78 | * @first_stream: pointer to the stream that was opened first | 77 | * @first_stream: pointer to the stream that was opened first |
79 | * @second_stream: pointer to second stream | 78 | * @second_stream: pointer to second stream |
80 | * @dev: struct device pointer | ||
81 | * @playback: the number of playback streams opened | 79 | * @playback: the number of playback streams opened |
82 | * @capture: the number of capture streams opened | 80 | * @capture: the number of capture streams opened |
83 | * @asynchronous: 0=synchronous mode, 1=asynchronous mode | 81 | * @asynchronous: 0=synchronous mode, 1=asynchronous mode |
84 | * @cpu_dai: the CPU DAI for this device | 82 | * @cpu_dai: the CPU DAI for this device |
85 | * @dev_attr: the sysfs device attribute structure | 83 | * @dev_attr: the sysfs device attribute structure |
86 | * @stats: SSI statistics | 84 | * @stats: SSI statistics |
85 | * @name: name for this device | ||
87 | */ | 86 | */ |
88 | struct fsl_ssi_private { | 87 | struct fsl_ssi_private { |
89 | char name[8]; | ||
90 | struct ccsr_ssi __iomem *ssi; | 88 | struct ccsr_ssi __iomem *ssi; |
91 | dma_addr_t ssi_phys; | 89 | dma_addr_t ssi_phys; |
92 | unsigned int irq; | 90 | unsigned int irq; |
93 | struct snd_pcm_substream *first_stream; | 91 | struct snd_pcm_substream *first_stream; |
94 | struct snd_pcm_substream *second_stream; | 92 | struct snd_pcm_substream *second_stream; |
95 | struct device *dev; | ||
96 | unsigned int playback; | 93 | unsigned int playback; |
97 | unsigned int capture; | 94 | unsigned int capture; |
98 | int asynchronous; | 95 | int asynchronous; |
99 | struct snd_soc_dai cpu_dai; | 96 | unsigned int fifo_depth; |
97 | struct snd_soc_dai_driver cpu_dai_drv; | ||
100 | struct device_attribute dev_attr; | 98 | struct device_attribute dev_attr; |
99 | struct platform_device *pdev; | ||
101 | 100 | ||
102 | struct { | 101 | struct { |
103 | unsigned int rfrc; | 102 | unsigned int rfrc; |
@@ -122,6 +121,8 @@ struct fsl_ssi_private { | |||
122 | unsigned int tfe1; | 121 | unsigned int tfe1; |
123 | unsigned int tfe0; | 122 | unsigned int tfe0; |
124 | } stats; | 123 | } stats; |
124 | |||
125 | char name[1]; | ||
125 | }; | 126 | }; |
126 | 127 | ||
127 | /** | 128 | /** |
@@ -280,7 +281,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
280 | struct snd_soc_dai *dai) | 281 | struct snd_soc_dai *dai) |
281 | { | 282 | { |
282 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 283 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
283 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | 284 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
284 | 285 | ||
285 | /* | 286 | /* |
286 | * If this is the first stream opened, then request the IRQ | 287 | * If this is the first stream opened, then request the IRQ |
@@ -290,6 +291,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
290 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 291 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
291 | int ret; | 292 | int ret; |
292 | 293 | ||
294 | /* The 'name' should not have any slashes in it. */ | ||
293 | ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, | 295 | ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, |
294 | ssi_private->name, ssi_private); | 296 | ssi_private->name, ssi_private); |
295 | if (ret < 0) { | 297 | if (ret < 0) { |
@@ -336,11 +338,20 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
336 | 338 | ||
337 | /* | 339 | /* |
338 | * Set the watermark for transmit FIFI 0 and receive FIFO 0. We | 340 | * Set the watermark for transmit FIFI 0 and receive FIFO 0. We |
339 | * don't use FIFO 1. Since the SSI only supports stereo, the | 341 | * don't use FIFO 1. We program the transmit water to signal a |
340 | * watermark should never be an odd number. | 342 | * DMA transfer if there are only two (or fewer) elements left |
343 | * in the FIFO. Two elements equals one frame (left channel, | ||
344 | * right channel). This value, however, depends on the depth of | ||
345 | * the transmit buffer. | ||
346 | * | ||
347 | * We program the receive FIFO to notify us if at least two | ||
348 | * elements (one frame) have been written to the FIFO. We could | ||
349 | * make this value larger (and maybe we should), but this way | ||
350 | * data will be written to memory as soon as it's available. | ||
341 | */ | 351 | */ |
342 | out_be32(&ssi->sfcsr, | 352 | out_be32(&ssi->sfcsr, |
343 | CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2)); | 353 | CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | |
354 | CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2)); | ||
344 | 355 | ||
345 | /* | 356 | /* |
346 | * We keep the SSI disabled because if we enable it, then the | 357 | * We keep the SSI disabled because if we enable it, then the |
@@ -422,7 +433,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
422 | static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, | 433 | 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) | 434 | struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) |
424 | { | 435 | { |
425 | struct fsl_ssi_private *ssi_private = cpu_dai->private_data; | 436 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); |
426 | 437 | ||
427 | if (substream == ssi_private->first_stream) { | 438 | if (substream == ssi_private->first_stream) { |
428 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 439 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
@@ -458,7 +469,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, | |||
458 | struct snd_soc_dai *dai) | 469 | struct snd_soc_dai *dai) |
459 | { | 470 | { |
460 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 471 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
461 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | 472 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
462 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 473 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
463 | 474 | ||
464 | switch (cmd) { | 475 | switch (cmd) { |
@@ -497,7 +508,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, | |||
497 | struct snd_soc_dai *dai) | 508 | struct snd_soc_dai *dai) |
498 | { | 509 | { |
499 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 510 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
500 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | 511 | struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
501 | 512 | ||
502 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 513 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
503 | ssi_private->playback--; | 514 | ssi_private->playback--; |
@@ -523,56 +534,15 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, | |||
523 | } | 534 | } |
524 | } | 535 | } |
525 | 536 | ||
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 = { | 537 | static struct snd_soc_dai_ops fsl_ssi_dai_ops = { |
567 | .startup = fsl_ssi_startup, | 538 | .startup = fsl_ssi_startup, |
568 | .hw_params = fsl_ssi_hw_params, | 539 | .hw_params = fsl_ssi_hw_params, |
569 | .shutdown = fsl_ssi_shutdown, | 540 | .shutdown = fsl_ssi_shutdown, |
570 | .trigger = fsl_ssi_trigger, | 541 | .trigger = fsl_ssi_trigger, |
571 | .set_sysclk = fsl_ssi_set_sysclk, | ||
572 | .set_fmt = fsl_ssi_set_fmt, | ||
573 | }; | 542 | }; |
574 | 543 | ||
575 | static struct snd_soc_dai fsl_ssi_dai_template = { | 544 | /* Template for the CPU dai driver structure */ |
545 | static struct snd_soc_dai_driver fsl_ssi_dai_template = { | ||
576 | .playback = { | 546 | .playback = { |
577 | /* The SSI does not support monaural audio. */ | 547 | /* The SSI does not support monaural audio. */ |
578 | .channels_min = 2, | 548 | .channels_min = 2, |
@@ -640,95 +610,195 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev, | |||
640 | } | 610 | } |
641 | 611 | ||
642 | /** | 612 | /** |
643 | * fsl_ssi_create_dai: create a snd_soc_dai structure | 613 | * 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 | */ | 614 | */ |
649 | struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | 615 | static void make_lowercase(char *s) |
616 | { | ||
617 | char *p = s; | ||
618 | char c; | ||
619 | |||
620 | while ((c = *p)) { | ||
621 | if ((c >= 'A') && (c <= 'Z')) | ||
622 | *p = c + ('a' - 'A'); | ||
623 | p++; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | static int __devinit fsl_ssi_probe(struct platform_device *pdev, | ||
628 | const struct of_device_id *match) | ||
650 | { | 629 | { |
651 | struct snd_soc_dai *fsl_ssi_dai; | ||
652 | struct fsl_ssi_private *ssi_private; | 630 | struct fsl_ssi_private *ssi_private; |
653 | int ret = 0; | 631 | int ret = 0; |
654 | struct device_attribute *dev_attr; | 632 | struct device_attribute *dev_attr = NULL; |
633 | struct device_node *np = pdev->dev.of_node; | ||
634 | const char *p, *sprop; | ||
635 | const uint32_t *iprop; | ||
636 | struct resource res; | ||
637 | char name[64]; | ||
638 | |||
639 | /* SSIs that are not connected on the board should have a | ||
640 | * status = "disabled" | ||
641 | * property in their device tree nodes. | ||
642 | */ | ||
643 | if (!of_device_is_available(np)) | ||
644 | return -ENODEV; | ||
645 | |||
646 | /* Check for a codec-handle property. */ | ||
647 | if (!of_get_property(np, "codec-handle", NULL)) { | ||
648 | dev_err(&pdev->dev, "missing codec-handle property\n"); | ||
649 | return -ENODEV; | ||
650 | } | ||
655 | 651 | ||
656 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); | 652 | /* We only support the SSI in "I2S Slave" mode */ |
653 | sprop = of_get_property(np, "fsl,mode", NULL); | ||
654 | if (!sprop || strcmp(sprop, "i2s-slave")) { | ||
655 | dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); | ||
656 | return -ENODEV; | ||
657 | } | ||
658 | |||
659 | /* The DAI name is the last part of the full name of the node. */ | ||
660 | p = strrchr(np->full_name, '/') + 1; | ||
661 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), | ||
662 | GFP_KERNEL); | ||
657 | if (!ssi_private) { | 663 | if (!ssi_private) { |
658 | dev_err(ssi_info->dev, "could not allocate DAI object\n"); | 664 | dev_err(&pdev->dev, "could not allocate DAI object\n"); |
659 | return NULL; | 665 | return -ENOMEM; |
660 | } | 666 | } |
661 | memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template, | ||
662 | sizeof(struct snd_soc_dai)); | ||
663 | 667 | ||
664 | fsl_ssi_dai = &ssi_private->cpu_dai; | 668 | strcpy(ssi_private->name, p); |
665 | dev_attr = &ssi_private->dev_attr; | ||
666 | 669 | ||
667 | sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); | 670 | /* Initialize this copy of the CPU DAI driver structure */ |
668 | ssi_private->ssi = ssi_info->ssi; | 671 | memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, |
669 | ssi_private->ssi_phys = ssi_info->ssi_phys; | 672 | sizeof(fsl_ssi_dai_template)); |
670 | ssi_private->irq = ssi_info->irq; | 673 | ssi_private->cpu_dai_drv.name = ssi_private->name; |
671 | ssi_private->dev = ssi_info->dev; | ||
672 | ssi_private->asynchronous = ssi_info->asynchronous; | ||
673 | 674 | ||
674 | dev_set_drvdata(ssi_private->dev, fsl_ssi_dai); | 675 | /* Get the addresses and IRQ */ |
676 | ret = of_address_to_resource(np, 0, &res); | ||
677 | if (ret) { | ||
678 | dev_err(&pdev->dev, "could not determine device resources\n"); | ||
679 | kfree(ssi_private); | ||
680 | return ret; | ||
681 | } | ||
682 | ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start); | ||
683 | ssi_private->ssi_phys = res.start; | ||
684 | ssi_private->irq = irq_of_parse_and_map(np, 0); | ||
685 | |||
686 | /* Are the RX and the TX clocks locked? */ | ||
687 | if (of_find_property(np, "fsl,ssi-asynchronous", NULL)) | ||
688 | ssi_private->asynchronous = 1; | ||
689 | else | ||
690 | ssi_private->cpu_dai_drv.symmetric_rates = 1; | ||
691 | |||
692 | /* Determine the FIFO depth. */ | ||
693 | iprop = of_get_property(np, "fsl,fifo-depth", NULL); | ||
694 | if (iprop) | ||
695 | ssi_private->fifo_depth = *iprop; | ||
696 | else | ||
697 | /* Older 8610 DTs didn't have the fifo-depth property */ | ||
698 | ssi_private->fifo_depth = 8; | ||
675 | 699 | ||
676 | /* Initialize the the device_attribute structure */ | 700 | /* Initialize the the device_attribute structure */ |
677 | dev_attr->attr.name = "ssi-stats"; | 701 | dev_attr = &ssi_private->dev_attr; |
702 | dev_attr->attr.name = "statistics"; | ||
678 | dev_attr->attr.mode = S_IRUGO; | 703 | dev_attr->attr.mode = S_IRUGO; |
679 | dev_attr->show = fsl_sysfs_ssi_show; | 704 | dev_attr->show = fsl_sysfs_ssi_show; |
680 | 705 | ||
681 | ret = device_create_file(ssi_private->dev, dev_attr); | 706 | ret = device_create_file(&pdev->dev, dev_attr); |
682 | if (ret) { | 707 | if (ret) { |
683 | dev_err(ssi_info->dev, "could not create sysfs %s file\n", | 708 | dev_err(&pdev->dev, "could not create sysfs %s file\n", |
684 | ssi_private->dev_attr.attr.name); | 709 | ssi_private->dev_attr.attr.name); |
685 | kfree(fsl_ssi_dai); | 710 | goto error; |
686 | return NULL; | ||
687 | } | 711 | } |
688 | 712 | ||
689 | fsl_ssi_dai->private_data = ssi_private; | 713 | /* Register with ASoC */ |
690 | fsl_ssi_dai->name = ssi_private->name; | 714 | dev_set_drvdata(&pdev->dev, ssi_private); |
691 | fsl_ssi_dai->id = ssi_info->id; | 715 | |
692 | fsl_ssi_dai->dev = ssi_info->dev; | 716 | ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv); |
693 | fsl_ssi_dai->symmetric_rates = 1; | 717 | if (ret) { |
718 | dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); | ||
719 | goto error; | ||
720 | } | ||
694 | 721 | ||
695 | ret = snd_soc_register_dai(fsl_ssi_dai); | 722 | /* Trigger the machine driver's probe function. The platform driver |
696 | if (ret != 0) { | 723 | * name of the machine driver is taken from the /model property of the |
697 | dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret); | 724 | * device tree. We also pass the address of the CPU DAI driver |
698 | kfree(fsl_ssi_dai); | 725 | * structure. |
699 | return NULL; | 726 | */ |
727 | sprop = of_get_property(of_find_node_by_path("/"), "model", NULL); | ||
728 | /* Sometimes the model name has a "fsl," prefix, so we strip that. */ | ||
729 | p = strrchr(sprop, ','); | ||
730 | if (p) | ||
731 | sprop = p + 1; | ||
732 | snprintf(name, sizeof(name), "snd-soc-%s", sprop); | ||
733 | make_lowercase(name); | ||
734 | |||
735 | ssi_private->pdev = | ||
736 | platform_device_register_data(&pdev->dev, name, 0, NULL, 0); | ||
737 | if (IS_ERR(ssi_private->pdev)) { | ||
738 | ret = PTR_ERR(ssi_private->pdev); | ||
739 | dev_err(&pdev->dev, "failed to register platform: %d\n", ret); | ||
740 | goto error; | ||
700 | } | 741 | } |
701 | 742 | ||
702 | return fsl_ssi_dai; | 743 | return 0; |
744 | |||
745 | error: | ||
746 | snd_soc_unregister_dai(&pdev->dev); | ||
747 | dev_set_drvdata(&pdev->dev, NULL); | ||
748 | if (dev_attr) | ||
749 | device_remove_file(&pdev->dev, dev_attr); | ||
750 | irq_dispose_mapping(ssi_private->irq); | ||
751 | iounmap(ssi_private->ssi); | ||
752 | kfree(ssi_private); | ||
753 | |||
754 | return ret; | ||
703 | } | 755 | } |
704 | EXPORT_SYMBOL_GPL(fsl_ssi_create_dai); | ||
705 | 756 | ||
706 | /** | 757 | static int fsl_ssi_remove(struct platform_device *pdev) |
707 | * fsl_ssi_destroy_dai: destroy the snd_soc_dai object | ||
708 | * | ||
709 | * This function undoes the operations of fsl_ssi_create_dai() | ||
710 | */ | ||
711 | void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) | ||
712 | { | 758 | { |
713 | struct fsl_ssi_private *ssi_private = | 759 | struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); |
714 | container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai); | ||
715 | |||
716 | device_remove_file(ssi_private->dev, &ssi_private->dev_attr); | ||
717 | 760 | ||
718 | snd_soc_unregister_dai(&ssi_private->cpu_dai); | 761 | platform_device_unregister(ssi_private->pdev); |
762 | snd_soc_unregister_dai(&pdev->dev); | ||
763 | device_remove_file(&pdev->dev, &ssi_private->dev_attr); | ||
719 | 764 | ||
720 | kfree(ssi_private); | 765 | kfree(ssi_private); |
766 | dev_set_drvdata(&pdev->dev, NULL); | ||
767 | |||
768 | return 0; | ||
721 | } | 769 | } |
722 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); | 770 | |
771 | static const struct of_device_id fsl_ssi_ids[] = { | ||
772 | { .compatible = "fsl,mpc8610-ssi", }, | ||
773 | {} | ||
774 | }; | ||
775 | MODULE_DEVICE_TABLE(of, fsl_ssi_ids); | ||
776 | |||
777 | static struct of_platform_driver fsl_ssi_driver = { | ||
778 | .driver = { | ||
779 | .name = "fsl-ssi-dai", | ||
780 | .owner = THIS_MODULE, | ||
781 | .of_match_table = fsl_ssi_ids, | ||
782 | }, | ||
783 | .probe = fsl_ssi_probe, | ||
784 | .remove = fsl_ssi_remove, | ||
785 | }; | ||
723 | 786 | ||
724 | static int __init fsl_ssi_init(void) | 787 | static int __init fsl_ssi_init(void) |
725 | { | 788 | { |
726 | printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); | 789 | printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); |
727 | 790 | ||
728 | return 0; | 791 | return of_register_platform_driver(&fsl_ssi_driver); |
729 | } | 792 | } |
793 | |||
794 | static void __exit fsl_ssi_exit(void) | ||
795 | { | ||
796 | of_unregister_platform_driver(&fsl_ssi_driver); | ||
797 | } | ||
798 | |||
730 | module_init(fsl_ssi_init); | 799 | module_init(fsl_ssi_init); |
800 | module_exit(fsl_ssi_exit); | ||
731 | 801 | ||
732 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 802 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
733 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | 803 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); |
734 | MODULE_LICENSE("GPL"); | 804 | 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 3dcd1469f283..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 platform_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 platform_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 platform_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 platform_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 ca99586f2ad9..a3c0cd5382fb 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 platform_device *op); | ||
85 | int mpc5200_audio_dma_destroy(struct platform_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 a9560235daee..40acc8e2b1ca 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c | |||
@@ -143,7 +143,7 @@ static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream, | |||
143 | struct snd_pcm_hw_params *params, | 143 | struct snd_pcm_hw_params *params, |
144 | struct snd_soc_dai *cpu_dai) | 144 | struct snd_soc_dai *cpu_dai) |
145 | { | 145 | { |
146 | struct psc_dma *psc_dma = cpu_dai->private_data; | 146 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
147 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); | 147 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); |
148 | 148 | ||
149 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" | 149 | dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i" |
@@ -166,7 +166,7 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, | |||
166 | struct snd_pcm_hw_params *params, | 166 | struct snd_pcm_hw_params *params, |
167 | struct snd_soc_dai *cpu_dai) | 167 | struct snd_soc_dai *cpu_dai) |
168 | { | 168 | { |
169 | struct psc_dma *psc_dma = cpu_dai->private_data; | 169 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
170 | 170 | ||
171 | dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); | 171 | dev_dbg(psc_dma->dev, "%s(substream=%p)\n", __func__, substream); |
172 | 172 | ||
@@ -181,8 +181,7 @@ static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream, | |||
181 | static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, | 181 | static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, |
182 | struct snd_soc_dai *dai) | 182 | struct snd_soc_dai *dai) |
183 | { | 183 | { |
184 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 184 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(dai); |
185 | struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; | ||
186 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); | 185 | struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); |
187 | 186 | ||
188 | switch (cmd) { | 187 | switch (cmd) { |
@@ -207,10 +206,9 @@ static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd, | |||
207 | return 0; | 206 | return 0; |
208 | } | 207 | } |
209 | 208 | ||
210 | static int psc_ac97_probe(struct platform_device *pdev, | 209 | static int psc_ac97_probe(struct snd_soc_dai *cpu_dai) |
211 | struct snd_soc_dai *cpu_dai) | ||
212 | { | 210 | { |
213 | struct psc_dma *psc_dma = cpu_dai->private_data; | 211 | struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai); |
214 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; | 212 | struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; |
215 | 213 | ||
216 | /* Go */ | 214 | /* Go */ |
@@ -237,9 +235,8 @@ static struct snd_soc_dai_ops psc_ac97_digital_ops = { | |||
237 | .hw_params = psc_ac97_hw_digital_params, | 235 | .hw_params = psc_ac97_hw_digital_params, |
238 | }; | 236 | }; |
239 | 237 | ||
240 | struct snd_soc_dai psc_ac97_dai[] = { | 238 | static struct snd_soc_dai_driver psc_ac97_dai[] = { |
241 | { | 239 | { |
242 | .name = "AC97", | ||
243 | .ac97_control = 1, | 240 | .ac97_control = 1, |
244 | .probe = psc_ac97_probe, | 241 | .probe = psc_ac97_probe, |
245 | .playback = { | 242 | .playback = { |
@@ -257,7 +254,6 @@ struct snd_soc_dai psc_ac97_dai[] = { | |||
257 | .ops = &psc_ac97_analog_ops, | 254 | .ops = &psc_ac97_analog_ops, |
258 | }, | 255 | }, |
259 | { | 256 | { |
260 | .name = "SPDIF", | ||
261 | .ac97_control = 1, | 257 | .ac97_control = 1, |
262 | .playback = { | 258 | .playback = { |
263 | .channels_min = 1, | 259 | .channels_min = 1, |
@@ -268,7 +264,6 @@ struct snd_soc_dai psc_ac97_dai[] = { | |||
268 | }, | 264 | }, |
269 | .ops = &psc_ac97_digital_ops, | 265 | .ops = &psc_ac97_digital_ops, |
270 | } }; | 266 | } }; |
271 | EXPORT_SYMBOL_GPL(psc_ac97_dai); | ||
272 | 267 | ||
273 | 268 | ||
274 | 269 | ||
@@ -280,18 +275,11 @@ EXPORT_SYMBOL_GPL(psc_ac97_dai); | |||
280 | static int __devinit psc_ac97_of_probe(struct platform_device *op, | 275 | static int __devinit psc_ac97_of_probe(struct platform_device *op, |
281 | const struct of_device_id *match) | 276 | const struct of_device_id *match) |
282 | { | 277 | { |
283 | int rc, i; | 278 | int rc; |
284 | struct snd_ac97 ac97; | 279 | struct snd_ac97 ac97; |
285 | struct mpc52xx_psc __iomem *regs; | 280 | struct mpc52xx_psc __iomem *regs; |
286 | 281 | ||
287 | rc = mpc5200_audio_dma_create(op); | 282 | rc = snd_soc_register_dais(&op->dev, psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); |
288 | if (rc != 0) | ||
289 | return rc; | ||
290 | |||
291 | for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) | ||
292 | psc_ac97_dai[i].dev = &op->dev; | ||
293 | |||
294 | rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai)); | ||
295 | if (rc != 0) { | 283 | if (rc != 0) { |
296 | dev_err(&op->dev, "Failed to register DAI\n"); | 284 | dev_err(&op->dev, "Failed to register DAI\n"); |
297 | return rc; | 285 | return rc; |
@@ -301,9 +289,6 @@ static int __devinit psc_ac97_of_probe(struct platform_device *op, | |||
301 | regs = psc_dma->psc_regs; | 289 | regs = psc_dma->psc_regs; |
302 | ac97.private_data = psc_dma; | 290 | ac97.private_data = psc_dma; |
303 | 291 | ||
304 | for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++) | ||
305 | psc_ac97_dai[i].private_data = psc_dma; | ||
306 | |||
307 | psc_dma->imr = 0; | 292 | psc_dma->imr = 0; |
308 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); | 293 | out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); |
309 | 294 | ||
@@ -319,7 +304,8 @@ static int __devinit psc_ac97_of_probe(struct platform_device *op, | |||
319 | 304 | ||
320 | static int __devexit psc_ac97_of_remove(struct platform_device *op) | 305 | static int __devexit psc_ac97_of_remove(struct platform_device *op) |
321 | { | 306 | { |
322 | return mpc5200_audio_dma_destroy(op); | 307 | snd_soc_unregister_dais(&op->dev, ARRAY_SIZE(psc_ac97_dai)); |
308 | return 0; | ||
323 | } | 309 | } |
324 | 310 | ||
325 | /* Match table for of_platform binding */ | 311 | /* 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 534f04cb15d7..74ffed41340f 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 platform_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 platform_device *op, | |||
207 | 201 | ||
208 | static int __devexit psc_i2s_of_remove(struct platform_device *op) | 202 | static int __devexit psc_i2s_of_remove(struct platform_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 3b13b8d65262..0d7dcf1e4863 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -1,85 +1,97 @@ | |||
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> | 16 | #include <linux/slab.h> |
17 | #include <sound/soc.h> | 17 | #include <sound/soc.h> |
18 | #include <asm/immap_86xx.h> | 18 | #include <asm/fsl_guts.h> |
19 | 19 | ||
20 | #include "../codecs/cs4270.h" | ||
21 | #include "fsl_dma.h" | 20 | #include "fsl_dma.h" |
22 | #include "fsl_ssi.h" | 21 | #include "fsl_ssi.h" |
23 | 22 | ||
23 | /* There's only one global utilities register */ | ||
24 | static phys_addr_t guts_phys; | ||
25 | |||
26 | #define DAI_NAME_SIZE 32 | ||
27 | |||
24 | /** | 28 | /** |
25 | * mpc8610_hpcd_data: fabric-specific ASoC device data | 29 | * mpc8610_hpcd_data: machine-specific ASoC device data |
26 | * | 30 | * |
27 | * This structure contains data for a single sound platform device on an | 31 | * 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. | 32 | * MPC8610 HPCD. Some of the data is taken from the device tree. |
29 | */ | 33 | */ |
30 | struct mpc8610_hpcd_data { | 34 | struct mpc8610_hpcd_data { |
31 | struct snd_soc_device sound_devdata; | 35 | struct snd_soc_dai_link dai[2]; |
32 | struct snd_soc_dai_link dai; | 36 | struct snd_soc_card card; |
33 | struct snd_soc_card machine; | ||
34 | unsigned int dai_format; | 37 | unsigned int dai_format; |
35 | unsigned int codec_clk_direction; | 38 | unsigned int codec_clk_direction; |
36 | unsigned int cpu_clk_direction; | 39 | unsigned int cpu_clk_direction; |
37 | unsigned int clk_frequency; | 40 | unsigned int clk_frequency; |
38 | struct ccsr_guts __iomem *guts; | 41 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ |
39 | struct ccsr_ssi __iomem *ssi; | 42 | 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*/ | 43 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ |
44 | char codec_dai_name[DAI_NAME_SIZE]; | ||
45 | char codec_name[DAI_NAME_SIZE]; | ||
46 | char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ | ||
46 | }; | 47 | }; |
47 | 48 | ||
48 | /** | 49 | /** |
49 | * mpc8610_hpcd_machine_probe: initialize the board | 50 | * mpc8610_hpcd_machine_probe: initialize the board |
50 | * | 51 | * |
51 | * This function is called when platform_device_add() is called. It is used | 52 | * This function is used to initialize the board-specific hardware. |
52 | * to initialize the board-specific hardware. | ||
53 | * | 53 | * |
54 | * Here we program the DMACR and PMUXCR registers. | 54 | * Here we program the DMACR and PMUXCR registers. |
55 | */ | 55 | */ |
56 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | 56 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) |
57 | { | 57 | { |
58 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
58 | struct mpc8610_hpcd_data *machine_data = | 59 | struct mpc8610_hpcd_data *machine_data = |
59 | sound_device->dev.platform_data; | 60 | container_of(card, struct mpc8610_hpcd_data, card); |
61 | struct ccsr_guts_86xx __iomem *guts; | ||
60 | 62 | ||
61 | /* Program the signal routing between the SSI and the DMA */ | 63 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); |
62 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 64 | if (!guts) { |
63 | machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); | 65 | dev_err(card->dev, "could not map global utilities\n"); |
64 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 66 | return -ENOMEM; |
65 | machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); | 67 | } |
66 | 68 | ||
67 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 69 | /* Program the signal routing between the SSI and the DMA */ |
68 | machine_data->dma_channel_id[0], 0); | 70 | guts_set_dmacr(guts, machine_data->dma_id[0], |
69 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 71 | machine_data->dma_channel_id[0], |
70 | machine_data->dma_channel_id[1], 0); | 72 | CCSR_GUTS_DMACR_DEV_SSI); |
73 | guts_set_dmacr(guts, machine_data->dma_id[1], | ||
74 | machine_data->dma_channel_id[1], | ||
75 | CCSR_GUTS_DMACR_DEV_SSI); | ||
76 | |||
77 | guts_set_pmuxcr_dma(guts, machine_data->dma_id[0], | ||
78 | machine_data->dma_channel_id[0], 0); | ||
79 | guts_set_pmuxcr_dma(guts, machine_data->dma_id[1], | ||
80 | machine_data->dma_channel_id[1], 0); | ||
71 | 81 | ||
72 | switch (machine_data->ssi_id) { | 82 | switch (machine_data->ssi_id) { |
73 | case 0: | 83 | case 0: |
74 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 84 | clrsetbits_be32(&guts->pmuxcr, |
75 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); | 85 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); |
76 | break; | 86 | break; |
77 | case 1: | 87 | case 1: |
78 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 88 | clrsetbits_be32(&guts->pmuxcr, |
79 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); | 89 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); |
80 | break; | 90 | break; |
81 | } | 91 | } |
82 | 92 | ||
93 | iounmap(guts); | ||
94 | |||
83 | return 0; | 95 | return 0; |
84 | } | 96 | } |
85 | 97 | ||
@@ -93,38 +105,15 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | |||
93 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | 105 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) |
94 | { | 106 | { |
95 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 107 | 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 = | 108 | struct mpc8610_hpcd_data *machine_data = |
99 | rtd->socdev->dev->platform_data; | 109 | container_of(rtd->card, struct mpc8610_hpcd_data, card); |
110 | struct device *dev = rtd->card->dev; | ||
100 | int ret = 0; | 111 | int ret = 0; |
101 | 112 | ||
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. */ | 113 | /* Tell the codec driver what the serial protocol is. */ |
111 | ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format); | 114 | ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format); |
112 | if (ret < 0) { | 115 | if (ret < 0) { |
113 | dev_err(substream->pcm->card->dev, | 116 | 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; | 117 | return ret; |
129 | } | 118 | } |
130 | 119 | ||
@@ -132,12 +121,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 | 121 | * Tell the codec driver what the MCLK frequency is, and whether it's |
133 | * a slave or master. | 122 | * a slave or master. |
134 | */ | 123 | */ |
135 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, | 124 | ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, |
136 | machine_data->clk_frequency, | 125 | machine_data->clk_frequency, |
137 | machine_data->codec_clk_direction); | 126 | machine_data->codec_clk_direction); |
138 | if (ret < 0) { | 127 | if (ret < 0) { |
139 | dev_err(substream->pcm->card->dev, | 128 | dev_err(dev, "could not set codec driver clock params\n"); |
140 | "could not set codec driver clock params\n"); | ||
141 | return ret; | 129 | return ret; |
142 | } | 130 | } |
143 | 131 | ||
@@ -150,116 +138,255 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | |||
150 | * This function is called to remove the sound device for one SSI. We | 138 | * This function is called to remove the sound device for one SSI. We |
151 | * de-program the DMACR and PMUXCR register. | 139 | * de-program the DMACR and PMUXCR register. |
152 | */ | 140 | */ |
153 | int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) | 141 | static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) |
154 | { | 142 | { |
143 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
155 | struct mpc8610_hpcd_data *machine_data = | 144 | struct mpc8610_hpcd_data *machine_data = |
156 | sound_device->dev.platform_data; | 145 | container_of(card, struct mpc8610_hpcd_data, card); |
146 | struct ccsr_guts_86xx __iomem *guts; | ||
147 | |||
148 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); | ||
149 | if (!guts) { | ||
150 | dev_err(card->dev, "could not map global utilities\n"); | ||
151 | return -ENOMEM; | ||
152 | } | ||
157 | 153 | ||
158 | /* Restore the signal routing */ | 154 | /* Restore the signal routing */ |
159 | 155 | ||
160 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 156 | guts_set_dmacr(guts, machine_data->dma_id[0], |
161 | machine_data->dma_channel_id[0], 0); | 157 | machine_data->dma_channel_id[0], 0); |
162 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 158 | guts_set_dmacr(guts, machine_data->dma_id[1], |
163 | machine_data->dma_channel_id[1], 0); | 159 | machine_data->dma_channel_id[1], 0); |
164 | 160 | ||
165 | switch (machine_data->ssi_id) { | 161 | switch (machine_data->ssi_id) { |
166 | case 0: | 162 | case 0: |
167 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 163 | clrsetbits_be32(&guts->pmuxcr, |
168 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); | 164 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); |
169 | break; | 165 | break; |
170 | case 1: | 166 | case 1: |
171 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 167 | clrsetbits_be32(&guts->pmuxcr, |
172 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); | 168 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); |
173 | break; | 169 | break; |
174 | } | 170 | } |
175 | 171 | ||
172 | iounmap(guts); | ||
173 | |||
176 | return 0; | 174 | return 0; |
177 | } | 175 | } |
178 | 176 | ||
179 | /** | 177 | /** |
180 | * mpc8610_hpcd_ops: ASoC fabric driver operations | 178 | * mpc8610_hpcd_ops: ASoC machine driver operations |
181 | */ | 179 | */ |
182 | static struct snd_soc_ops mpc8610_hpcd_ops = { | 180 | static struct snd_soc_ops mpc8610_hpcd_ops = { |
183 | .startup = mpc8610_hpcd_startup, | 181 | .startup = mpc8610_hpcd_startup, |
184 | }; | 182 | }; |
185 | 183 | ||
186 | /** | 184 | /** |
187 | * mpc8610_hpcd_probe: OF probe function for the fabric driver | 185 | * get_node_by_phandle_name - get a node by its phandle name |
188 | * | 186 | * |
189 | * This function gets called when an SSI node is found in the device tree. | 187 | * This function takes a node, the name of a property in that node, and a |
188 | * compatible string. Assuming the property is a phandle to another node, | ||
189 | * it returns that node, (optionally) if that node is compatible. | ||
190 | * | 190 | * |
191 | * Although this is a fabric driver, the SSI node is the "master" node with | 191 | * 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 | 192 | * with the specific string, then NULL is returned. |
193 | * device for each new SSI node that has a codec attached. | 193 | */ |
194 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
195 | const char *name, | ||
196 | const char *compatible) | ||
197 | { | ||
198 | const phandle *ph; | ||
199 | int len; | ||
200 | |||
201 | ph = of_get_property(np, name, &len); | ||
202 | if (!ph || (len != sizeof(phandle))) | ||
203 | return NULL; | ||
204 | |||
205 | np = of_find_node_by_phandle(*ph); | ||
206 | if (!np) | ||
207 | return NULL; | ||
208 | |||
209 | if (compatible && !of_device_is_compatible(np, compatible)) { | ||
210 | of_node_put(np); | ||
211 | return NULL; | ||
212 | } | ||
213 | |||
214 | return np; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
219 | * | ||
220 | * Return the value of the cell-index property of the parent of the given | ||
221 | * node. This is used for DMA channel nodes that need to know the DMA ID | ||
222 | * of the controller they are on. | ||
223 | */ | ||
224 | static int get_parent_cell_index(struct device_node *np) | ||
225 | { | ||
226 | struct device_node *parent = of_get_parent(np); | ||
227 | const u32 *iprop; | ||
228 | |||
229 | if (!parent) | ||
230 | return -1; | ||
231 | |||
232 | iprop = of_get_property(parent, "cell-index", NULL); | ||
233 | of_node_put(parent); | ||
234 | |||
235 | if (!iprop) | ||
236 | return -1; | ||
237 | |||
238 | return *iprop; | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * codec_node_dev_name - determine the dev_name for a codec node | ||
194 | * | 243 | * |
195 | * FIXME: Currently, we only support one DMA controller, so if there are | 244 | * This function determines the dev_name for an I2C node. This is the name |
196 | * multiple SSI nodes with codecs, only the first will be supported. | 245 | * that would be returned by dev_name() if this device_node were part of a |
246 | * 'struct device' It's ugly and hackish, but it works. | ||
197 | * | 247 | * |
198 | * FIXME: Even if we did support multiple DMA controllers, we have no | 248 | * The dev_name for such devices include the bus number and I2C address. For |
199 | * mechanism for assigning DMA controllers and channels to the individual | 249 | * example, "cs4270-codec.0-004f". |
200 | * SSI devices. We also probably aren't compatible with the generic Elo DMA | ||
201 | * device driver. | ||
202 | */ | 250 | */ |
203 | static int mpc8610_hpcd_probe(struct platform_device *ofdev, | 251 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) |
204 | const struct of_device_id *match) | ||
205 | { | 252 | { |
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; | 253 | const u32 *iprop; |
254 | int bus, addr; | ||
255 | char temp[DAI_NAME_SIZE]; | ||
256 | |||
257 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
258 | |||
259 | iprop = of_get_property(np, "reg", NULL); | ||
260 | if (!iprop) | ||
261 | return -EINVAL; | ||
262 | |||
263 | addr = *iprop; | ||
264 | |||
265 | bus = get_parent_cell_index(np); | ||
266 | if (bus < 0) | ||
267 | return bus; | ||
268 | |||
269 | snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static int get_dma_channel(struct device_node *ssi_np, | ||
275 | const char *compatible, | ||
276 | struct snd_soc_dai_link *dai, | ||
277 | unsigned int *dma_channel_id, | ||
278 | unsigned int *dma_id) | ||
279 | { | ||
214 | struct resource res; | 280 | struct resource res; |
281 | struct device_node *dma_channel_np; | ||
282 | const u32 *iprop; | ||
283 | int ret; | ||
284 | |||
285 | dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, | ||
286 | "fsl,ssi-dma-channel"); | ||
287 | if (!dma_channel_np) | ||
288 | return -EINVAL; | ||
289 | |||
290 | /* Determine the dev_name for the device_node. This code mimics the | ||
291 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
292 | * the dev_name() of the device to match the platform (DMA) device with | ||
293 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
294 | * now). | ||
295 | * | ||
296 | * dai->platform name should already point to an allocated buffer. | ||
297 | */ | ||
298 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
299 | if (ret) | ||
300 | return ret; | ||
301 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
302 | (unsigned long long) res.start, dma_channel_np->name); | ||
303 | |||
304 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
305 | if (!iprop) { | ||
306 | of_node_put(dma_channel_np); | ||
307 | return -EINVAL; | ||
308 | } | ||
309 | |||
310 | *dma_channel_id = *iprop; | ||
311 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
312 | of_node_put(dma_channel_np); | ||
313 | |||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * mpc8610_hpcd_probe: platform probe function for the machine driver | ||
319 | * | ||
320 | * Although this is a machine driver, the SSI node is the "master" node with | ||
321 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
322 | * device for each new SSI node that has a codec attached. | ||
323 | */ | ||
324 | static int mpc8610_hpcd_probe(struct platform_device *pdev) | ||
325 | { | ||
326 | struct device *dev = pdev->dev.parent; | ||
327 | /* ssi_pdev is the platform device for the SSI node that probed us */ | ||
328 | struct platform_device *ssi_pdev = | ||
329 | container_of(dev, struct platform_device, dev); | ||
330 | struct device_node *np = ssi_pdev->dev.of_node; | ||
331 | struct device_node *codec_np = NULL; | ||
215 | struct platform_device *sound_device = NULL; | 332 | struct platform_device *sound_device = NULL; |
216 | struct mpc8610_hpcd_data *machine_data; | 333 | struct mpc8610_hpcd_data *machine_data; |
217 | struct fsl_ssi_info ssi_info; | ||
218 | struct fsl_dma_info dma_info; | ||
219 | int ret = -ENODEV; | 334 | int ret = -ENODEV; |
220 | unsigned int playback_dma_channel; | 335 | const char *sprop; |
221 | unsigned int capture_dma_channel; | 336 | const u32 *iprop; |
337 | |||
338 | /* We are only interested in SSIs with a codec phandle in them, | ||
339 | * so let's make sure this SSI has one. The MPC8610 HPCD only | ||
340 | * knows about the CS4270 codec, so reject anything else. | ||
341 | */ | ||
342 | codec_np = get_node_by_phandle_name(np, "codec-handle", | ||
343 | "cirrus,cs4270"); | ||
344 | if (!codec_np) { | ||
345 | dev_err(dev, "invalid codec node\n"); | ||
346 | return -EINVAL; | ||
347 | } | ||
222 | 348 | ||
223 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); | 349 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); |
224 | if (!machine_data) | 350 | if (!machine_data) |
225 | return -ENOMEM; | 351 | return -ENOMEM; |
226 | 352 | ||
227 | memset(&ssi_info, 0, sizeof(ssi_info)); | 353 | machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); |
228 | memset(&dma_info, 0, sizeof(dma_info)); | 354 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; |
229 | 355 | ||
230 | ssi_info.dev = &ofdev->dev; | 356 | /* Determine the codec name, it will be used as the codec DAI name */ |
231 | 357 | ret = codec_node_dev_name(codec_np, machine_data->codec_name, | |
232 | /* | 358 | DAI_NAME_SIZE); |
233 | * We are only interested in SSIs with a codec phandle in them, so let's | 359 | if (ret) { |
234 | * make sure this SSI has one. | 360 | dev_err(&pdev->dev, "invalid codec node %s\n", |
235 | */ | 361 | codec_np->full_name); |
236 | codec_ph = of_get_property(np, "codec-handle", NULL); | 362 | ret = -EINVAL; |
237 | if (!codec_ph) | ||
238 | goto error; | 363 | goto error; |
364 | } | ||
365 | machine_data->dai[0].codec_name = machine_data->codec_name; | ||
239 | 366 | ||
240 | codec_np = of_find_node_by_phandle(*codec_ph); | 367 | /* The DAI name from the codec (snd_soc_dai_driver.name) */ |
241 | if (!codec_np) | 368 | machine_data->dai[0].codec_dai_name = "cs4270-hifi"; |
242 | goto error; | ||
243 | 369 | ||
244 | /* The MPC8610 HPCD only knows about the CS4270 codec, so reject | 370 | /* We register two DAIs per SSI, one for playback and the other for |
245 | anything else. */ | 371 | * capture. Currently, we only support codecs that have one DAI for |
246 | if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) | 372 | * both playback and capture. |
247 | goto error; | 373 | */ |
374 | memcpy(&machine_data->dai[1], &machine_data->dai[0], | ||
375 | sizeof(struct snd_soc_dai_link)); | ||
248 | 376 | ||
249 | /* Get the device ID */ | 377 | /* Get the device ID */ |
250 | iprop = of_get_property(np, "cell-index", NULL); | 378 | iprop = of_get_property(np, "cell-index", NULL); |
251 | if (!iprop) { | 379 | if (!iprop) { |
252 | dev_err(&ofdev->dev, "cell-index property not found\n"); | 380 | dev_err(&pdev->dev, "cell-index property not found\n"); |
253 | ret = -EINVAL; | 381 | ret = -EINVAL; |
254 | goto error; | 382 | goto error; |
255 | } | 383 | } |
256 | machine_data->ssi_id = *iprop; | 384 | machine_data->ssi_id = *iprop; |
257 | ssi_info.id = *iprop; | ||
258 | 385 | ||
259 | /* Get the serial format and clock direction. */ | 386 | /* Get the serial format and clock direction. */ |
260 | sprop = of_get_property(np, "fsl,mode", NULL); | 387 | sprop = of_get_property(np, "fsl,mode", NULL); |
261 | if (!sprop) { | 388 | if (!sprop) { |
262 | dev_err(&ofdev->dev, "fsl,mode property not found\n"); | 389 | dev_err(&pdev->dev, "fsl,mode property not found\n"); |
263 | ret = -EINVAL; | 390 | ret = -EINVAL; |
264 | goto error; | 391 | goto error; |
265 | } | 392 | } |
@@ -269,15 +396,14 @@ static int mpc8610_hpcd_probe(struct platform_device *ofdev, | |||
269 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | 396 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; |
270 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | 397 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; |
271 | 398 | ||
272 | /* | 399 | /* 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 | 400 | * need to get the frequency from the device tree and pass it to |
275 | * the codec driver. | 401 | * the codec driver. |
276 | */ | 402 | */ |
277 | iprop = of_get_property(codec_np, "clock-frequency", NULL); | 403 | iprop = of_get_property(codec_np, "clock-frequency", NULL); |
278 | if (!iprop || !*iprop) { | 404 | if (!iprop || !*iprop) { |
279 | dev_err(&ofdev->dev, "codec bus-frequency property " | 405 | dev_err(&pdev->dev, "codec bus-frequency " |
280 | "is missing or invalid\n"); | 406 | "property is missing or invalid\n"); |
281 | ret = -EINVAL; | 407 | ret = -EINVAL; |
282 | goto error; | 408 | goto error; |
283 | } | 409 | } |
@@ -311,317 +437,153 @@ static int mpc8610_hpcd_probe(struct platform_device *ofdev, | |||
311 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | 437 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; |
312 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | 438 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; |
313 | } else { | 439 | } else { |
314 | dev_err(&ofdev->dev, | 440 | dev_err(&pdev->dev, |
315 | "unrecognized fsl,mode property \"%s\"\n", sprop); | 441 | "unrecognized fsl,mode property '%s'\n", sprop); |
316 | ret = -EINVAL; | 442 | ret = -EINVAL; |
317 | goto error; | 443 | goto error; |
318 | } | 444 | } |
319 | 445 | ||
320 | if (!machine_data->clk_frequency) { | 446 | if (!machine_data->clk_frequency) { |
321 | dev_err(&ofdev->dev, "unknown clock frequency\n"); | 447 | dev_err(&pdev->dev, "unknown clock frequency\n"); |
322 | ret = -EINVAL; | 448 | ret = -EINVAL; |
323 | goto error; | 449 | goto error; |
324 | } | 450 | } |
325 | 451 | ||
326 | /* Read the SSI information from the device tree */ | 452 | /* Find the playback DMA channel to use. */ |
327 | ret = of_address_to_resource(np, 0, &res); | 453 | machine_data->dai[0].platform_name = machine_data->platform_name[0]; |
454 | ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], | ||
455 | &machine_data->dma_channel_id[0], | ||
456 | &machine_data->dma_id[0]); | ||
328 | if (ret) { | 457 | if (ret) { |
329 | dev_err(&ofdev->dev, "could not obtain SSI address\n"); | 458 | 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; | 459 | goto error; |
429 | } | 460 | } |
430 | 461 | ||
431 | dma_info.ssi_stx_phys = ssi_info.ssi_phys + | 462 | /* Find the capture DMA channel to use. */ |
432 | offsetof(struct ccsr_ssi, stx0); | 463 | machine_data->dai[1].platform_name = machine_data->platform_name[1]; |
433 | dma_info.ssi_srx_phys = ssi_info.ssi_phys + | 464 | ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], |
434 | offsetof(struct ccsr_ssi, srx0); | 465 | &machine_data->dma_channel_id[1], |
435 | 466 | &machine_data->dma_id[1]); | |
436 | /* We have the DMA information, so tell the DMA driver what it is */ | 467 | if (ret) { |
437 | if (!fsl_dma_configure(&dma_info)) { | 468 | 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; | 469 | goto error; |
441 | } | 470 | } |
442 | 471 | ||
443 | /* | 472 | /* Initialize our DAI data structure. */ |
444 | * Initialize our DAI data structure. We should probably get this | 473 | machine_data->dai[0].stream_name = "playback"; |
445 | * information from the device tree. | 474 | machine_data->dai[1].stream_name = "capture"; |
446 | */ | 475 | machine_data->dai[0].name = machine_data->dai[0].stream_name; |
447 | machine_data->dai.name = "CS4270"; | 476 | 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 | 477 | ||
454 | machine_data->machine.probe = mpc8610_hpcd_machine_probe; | 478 | machine_data->card.probe = mpc8610_hpcd_machine_probe; |
455 | machine_data->machine.remove = mpc8610_hpcd_machine_remove; | 479 | machine_data->card.remove = mpc8610_hpcd_machine_remove; |
456 | machine_data->machine.name = "MPC8610 HPCD"; | 480 | machine_data->card.name = pdev->name; /* The platform driver name */ |
457 | machine_data->machine.num_links = 1; | 481 | machine_data->card.num_links = 2; |
458 | machine_data->machine.dai_link = &machine_data->dai; | 482 | machine_data->card.dai_link = machine_data->dai; |
459 | 483 | ||
460 | /* Allocate a new audio platform device structure */ | 484 | /* Allocate a new audio platform device structure */ |
461 | sound_device = platform_device_alloc("soc-audio", -1); | 485 | sound_device = platform_device_alloc("soc-audio", -1); |
462 | if (!sound_device) { | 486 | if (!sound_device) { |
463 | dev_err(&ofdev->dev, "platform device allocation failed\n"); | 487 | dev_err(&pdev->dev, "platform device alloc failed\n"); |
464 | ret = -ENOMEM; | 488 | ret = -ENOMEM; |
465 | goto error; | 489 | goto error; |
466 | } | 490 | } |
467 | 491 | ||
468 | machine_data->sound_devdata.card = &machine_data->machine; | 492 | /* Associate the card data with the sound device */ |
469 | machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; | 493 | 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 | 494 | ||
475 | /* Set the platform device and ASoC device to point to each other */ | 495 | /* 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); | 496 | ret = platform_device_add(sound_device); |
484 | |||
485 | if (ret) { | 497 | if (ret) { |
486 | dev_err(&ofdev->dev, "platform device add failed\n"); | 498 | dev_err(&pdev->dev, "platform device add failed\n"); |
487 | goto error; | 499 | goto error; |
488 | } | 500 | } |
489 | 501 | ||
490 | dev_set_drvdata(&ofdev->dev, sound_device); | 502 | of_node_put(codec_np); |
491 | 503 | ||
492 | return 0; | 504 | return 0; |
493 | 505 | ||
494 | error: | 506 | error: |
495 | of_node_put(codec_np); | 507 | 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 | 508 | ||
500 | if (sound_device) | 509 | if (sound_device) |
501 | platform_device_unregister(sound_device); | 510 | platform_device_unregister(sound_device); |
502 | 511 | ||
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); | 512 | kfree(machine_data); |
528 | 513 | ||
529 | return ret; | 514 | return ret; |
530 | } | 515 | } |
531 | 516 | ||
532 | /** | 517 | /** |
533 | * mpc8610_hpcd_remove: remove the OF device | 518 | * mpc8610_hpcd_remove: remove the platform device |
534 | * | 519 | * |
535 | * This function is called when the OF device is removed. | 520 | * This function is called when the platform device is removed. |
536 | */ | 521 | */ |
537 | static int mpc8610_hpcd_remove(struct platform_device *ofdev) | 522 | static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev) |
538 | { | 523 | { |
539 | struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); | 524 | struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); |
525 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
540 | struct mpc8610_hpcd_data *machine_data = | 526 | struct mpc8610_hpcd_data *machine_data = |
541 | sound_device->dev.platform_data; | 527 | container_of(card, struct mpc8610_hpcd_data, card); |
542 | 528 | ||
543 | platform_device_unregister(sound_device); | 529 | platform_device_unregister(sound_device); |
544 | 530 | ||
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); | 531 | kfree(machine_data); |
567 | sound_device->dev.platform_data = NULL; | 532 | sound_device->dev.platform_data = NULL; |
568 | 533 | ||
569 | dev_set_drvdata(&ofdev->dev, NULL); | 534 | dev_set_drvdata(&pdev->dev, NULL); |
570 | 535 | ||
571 | return 0; | 536 | return 0; |
572 | } | 537 | } |
573 | 538 | ||
574 | static struct of_device_id mpc8610_hpcd_match[] = { | 539 | static struct platform_driver mpc8610_hpcd_driver = { |
575 | { | 540 | .probe = mpc8610_hpcd_probe, |
576 | .compatible = "fsl,mpc8610-ssi", | 541 | .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 = { | 542 | .driver = { |
584 | .name = "mpc8610_hpcd", | 543 | /* The name must match the 'model' property in the device tree, |
544 | * in lowercase letters. | ||
545 | */ | ||
546 | .name = "snd-soc-mpc8610hpcd", | ||
585 | .owner = THIS_MODULE, | 547 | .owner = THIS_MODULE, |
586 | .of_match_table = mpc8610_hpcd_match, | ||
587 | }, | 548 | }, |
588 | .probe = mpc8610_hpcd_probe, | ||
589 | .remove = mpc8610_hpcd_remove, | ||
590 | }; | 549 | }; |
591 | 550 | ||
592 | /** | 551 | /** |
593 | * mpc8610_hpcd_init: fabric driver initialization. | 552 | * mpc8610_hpcd_init: machine driver initialization. |
594 | * | 553 | * |
595 | * This function is called when this module is loaded. | 554 | * This function is called when this module is loaded. |
596 | */ | 555 | */ |
597 | static int __init mpc8610_hpcd_init(void) | 556 | static int __init mpc8610_hpcd_init(void) |
598 | { | 557 | { |
599 | int ret; | 558 | struct device_node *guts_np; |
600 | 559 | struct resource res; | |
601 | printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); | ||
602 | 560 | ||
603 | ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); | 561 | pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); |
604 | 562 | ||
605 | if (ret) | 563 | /* Get the physical address of the global utilities registers */ |
606 | printk(KERN_ERR | 564 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); |
607 | "mpc8610-hpcd: failed to register platform driver\n"); | 565 | if (of_address_to_resource(guts_np, 0, &res)) { |
566 | pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); | ||
567 | return -EINVAL; | ||
568 | } | ||
569 | guts_phys = res.start; | ||
608 | 570 | ||
609 | return ret; | 571 | return platform_driver_register(&mpc8610_hpcd_driver); |
610 | } | 572 | } |
611 | 573 | ||
612 | /** | 574 | /** |
613 | * mpc8610_hpcd_exit: fabric driver exit | 575 | * mpc8610_hpcd_exit: machine driver exit |
614 | * | 576 | * |
615 | * This function is called when this driver is unloaded. | 577 | * This function is called when this driver is unloaded. |
616 | */ | 578 | */ |
617 | static void __exit mpc8610_hpcd_exit(void) | 579 | static void __exit mpc8610_hpcd_exit(void) |
618 | { | 580 | { |
619 | of_unregister_platform_driver(&mpc8610_hpcd_of_driver); | 581 | platform_driver_unregister(&mpc8610_hpcd_driver); |
620 | } | 582 | } |
621 | 583 | ||
622 | module_init(mpc8610_hpcd_init); | 584 | module_init(mpc8610_hpcd_init); |
623 | module_exit(mpc8610_hpcd_exit); | 585 | module_exit(mpc8610_hpcd_exit); |
624 | 586 | ||
625 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 587 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
626 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); | 588 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); |
627 | MODULE_LICENSE("GPL"); | 589 | MODULE_LICENSE("GPL v2"); |
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c new file mode 100644 index 000000000000..63b9eaa1ebc2 --- /dev/null +++ b/sound/soc/fsl/p1022_ds.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /** | ||
2 | * Freescale P1022DS ALSA SoC Machine driver | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2010 Freescale Semiconductor, Inc. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/of_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <sound/soc.h> | ||
18 | #include <asm/fsl_guts.h> | ||
19 | |||
20 | #include "fsl_dma.h" | ||
21 | #include "fsl_ssi.h" | ||
22 | |||
23 | /* P1022-specific PMUXCR and DMUXCR bit definitions */ | ||
24 | |||
25 | #define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK 0x0001c000 | ||
26 | #define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI 0x00010000 | ||
27 | #define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI 0x00018000 | ||
28 | |||
29 | #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK 0x00000c00 | ||
30 | #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI 0x00000000 | ||
31 | |||
32 | #define CCSR_GUTS_DMUXCR_PAD 1 /* DMA controller/channel set to pad */ | ||
33 | #define CCSR_GUTS_DMUXCR_SSI 2 /* DMA controller/channel set to SSI */ | ||
34 | |||
35 | /* | ||
36 | * Set the DMACR register in the GUTS | ||
37 | * | ||
38 | * The DMACR register determines the source of initiated transfers for each | ||
39 | * channel on each DMA controller. Rather than have a bunch of repetitive | ||
40 | * macros for the bit patterns, we just have a function that calculates | ||
41 | * them. | ||
42 | * | ||
43 | * guts: Pointer to GUTS structure | ||
44 | * co: The DMA controller (0 or 1) | ||
45 | * ch: The channel on the DMA controller (0, 1, 2, or 3) | ||
46 | * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx) | ||
47 | */ | ||
48 | static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem *guts, | ||
49 | unsigned int co, unsigned int ch, unsigned int device) | ||
50 | { | ||
51 | unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); | ||
52 | |||
53 | clrsetbits_be32(&guts->dmuxcr, 3 << shift, device << shift); | ||
54 | } | ||
55 | |||
56 | /* There's only one global utilities register */ | ||
57 | static phys_addr_t guts_phys; | ||
58 | |||
59 | #define DAI_NAME_SIZE 32 | ||
60 | |||
61 | /** | ||
62 | * machine_data: machine-specific ASoC device data | ||
63 | * | ||
64 | * This structure contains data for a single sound platform device on an | ||
65 | * P1022 DS. Some of the data is taken from the device tree. | ||
66 | */ | ||
67 | struct machine_data { | ||
68 | struct snd_soc_dai_link dai[2]; | ||
69 | struct snd_soc_card card; | ||
70 | unsigned int dai_format; | ||
71 | unsigned int codec_clk_direction; | ||
72 | unsigned int cpu_clk_direction; | ||
73 | unsigned int clk_frequency; | ||
74 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ | ||
75 | unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ | ||
76 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ | ||
77 | char codec_name[DAI_NAME_SIZE]; | ||
78 | char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ | ||
79 | }; | ||
80 | |||
81 | /** | ||
82 | * p1022_ds_machine_probe: initialize the board | ||
83 | * | ||
84 | * This function is used to initialize the board-specific hardware. | ||
85 | * | ||
86 | * Here we program the DMACR and PMUXCR registers. | ||
87 | */ | ||
88 | static int p1022_ds_machine_probe(struct platform_device *sound_device) | ||
89 | { | ||
90 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
91 | struct machine_data *mdata = | ||
92 | container_of(card, struct machine_data, card); | ||
93 | struct ccsr_guts_85xx __iomem *guts; | ||
94 | |||
95 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); | ||
96 | if (!guts) { | ||
97 | dev_err(card->dev, "could not map global utilities\n"); | ||
98 | return -ENOMEM; | ||
99 | } | ||
100 | |||
101 | /* Enable SSI Tx signal */ | ||
102 | clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK, | ||
103 | CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI); | ||
104 | |||
105 | /* Enable SSI Rx signal */ | ||
106 | clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK, | ||
107 | CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI); | ||
108 | |||
109 | /* Enable DMA Channel for SSI */ | ||
110 | guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], | ||
111 | CCSR_GUTS_DMUXCR_SSI); | ||
112 | |||
113 | guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], | ||
114 | CCSR_GUTS_DMUXCR_SSI); | ||
115 | |||
116 | iounmap(guts); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * p1022_ds_startup: program the board with various hardware parameters | ||
123 | * | ||
124 | * This function takes board-specific information, like clock frequencies | ||
125 | * and serial data formats, and passes that information to the codec and | ||
126 | * transport drivers. | ||
127 | */ | ||
128 | static int p1022_ds_startup(struct snd_pcm_substream *substream) | ||
129 | { | ||
130 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
131 | struct machine_data *mdata = | ||
132 | container_of(rtd->card, struct machine_data, card); | ||
133 | struct device *dev = rtd->card->dev; | ||
134 | int ret = 0; | ||
135 | |||
136 | /* Tell the codec driver what the serial protocol is. */ | ||
137 | ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format); | ||
138 | if (ret < 0) { | ||
139 | dev_err(dev, "could not set codec driver audio format\n"); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Tell the codec driver what the MCLK frequency is, and whether it's | ||
145 | * a slave or master. | ||
146 | */ | ||
147 | ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, mdata->clk_frequency, | ||
148 | mdata->codec_clk_direction); | ||
149 | if (ret < 0) { | ||
150 | dev_err(dev, "could not set codec driver clock params\n"); | ||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * p1022_ds_machine_remove: Remove the sound device | ||
159 | * | ||
160 | * This function is called to remove the sound device for one SSI. We | ||
161 | * de-program the DMACR and PMUXCR register. | ||
162 | */ | ||
163 | static int p1022_ds_machine_remove(struct platform_device *sound_device) | ||
164 | { | ||
165 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
166 | struct machine_data *mdata = | ||
167 | container_of(card, struct machine_data, card); | ||
168 | struct ccsr_guts_85xx __iomem *guts; | ||
169 | |||
170 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); | ||
171 | if (!guts) { | ||
172 | dev_err(card->dev, "could not map global utilities\n"); | ||
173 | return -ENOMEM; | ||
174 | } | ||
175 | |||
176 | /* Restore the signal routing */ | ||
177 | clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK); | ||
178 | clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK); | ||
179 | guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], 0); | ||
180 | guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], 0); | ||
181 | |||
182 | iounmap(guts); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * p1022_ds_ops: ASoC machine driver operations | ||
189 | */ | ||
190 | static struct snd_soc_ops p1022_ds_ops = { | ||
191 | .startup = p1022_ds_startup, | ||
192 | }; | ||
193 | |||
194 | /** | ||
195 | * get_node_by_phandle_name - get a node by its phandle name | ||
196 | * | ||
197 | * This function takes a node, the name of a property in that node, and a | ||
198 | * compatible string. Assuming the property is a phandle to another node, | ||
199 | * it returns that node, (optionally) if that node is compatible. | ||
200 | * | ||
201 | * If the property is not a phandle, or the node it points to is not compatible | ||
202 | * with the specific string, then NULL is returned. | ||
203 | */ | ||
204 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
205 | const char *name, const char *compatible) | ||
206 | { | ||
207 | np = of_parse_phandle(np, name, 0); | ||
208 | if (!np) | ||
209 | return NULL; | ||
210 | |||
211 | if (!of_device_is_compatible(np, compatible)) { | ||
212 | of_node_put(np); | ||
213 | return NULL; | ||
214 | } | ||
215 | |||
216 | return np; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
221 | * | ||
222 | * Return the value of the cell-index property of the parent of the given | ||
223 | * node. This is used for DMA channel nodes that need to know the DMA ID | ||
224 | * of the controller they are on. | ||
225 | */ | ||
226 | static int get_parent_cell_index(struct device_node *np) | ||
227 | { | ||
228 | struct device_node *parent = of_get_parent(np); | ||
229 | const u32 *iprop; | ||
230 | int ret = -1; | ||
231 | |||
232 | if (!parent) | ||
233 | return -1; | ||
234 | |||
235 | iprop = of_get_property(parent, "cell-index", NULL); | ||
236 | if (iprop) | ||
237 | ret = *iprop; | ||
238 | |||
239 | of_node_put(parent); | ||
240 | |||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * codec_node_dev_name - determine the dev_name for a codec node | ||
246 | * | ||
247 | * This function determines the dev_name for an I2C node. This is the name | ||
248 | * that would be returned by dev_name() if this device_node were part of a | ||
249 | * 'struct device' It's ugly and hackish, but it works. | ||
250 | * | ||
251 | * The dev_name for such devices include the bus number and I2C address. For | ||
252 | * example, "cs4270-codec.0-004f". | ||
253 | */ | ||
254 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) | ||
255 | { | ||
256 | const u32 *iprop; | ||
257 | int bus, addr; | ||
258 | char temp[DAI_NAME_SIZE]; | ||
259 | |||
260 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
261 | |||
262 | iprop = of_get_property(np, "reg", NULL); | ||
263 | if (!iprop) | ||
264 | return -EINVAL; | ||
265 | |||
266 | addr = *iprop; | ||
267 | |||
268 | bus = get_parent_cell_index(np); | ||
269 | if (bus < 0) | ||
270 | return bus; | ||
271 | |||
272 | snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int get_dma_channel(struct device_node *ssi_np, | ||
278 | const char *compatible, | ||
279 | struct snd_soc_dai_link *dai, | ||
280 | unsigned int *dma_channel_id, | ||
281 | unsigned int *dma_id) | ||
282 | { | ||
283 | struct resource res; | ||
284 | struct device_node *dma_channel_np; | ||
285 | const u32 *iprop; | ||
286 | int ret; | ||
287 | |||
288 | dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, | ||
289 | "fsl,ssi-dma-channel"); | ||
290 | if (!dma_channel_np) | ||
291 | return -EINVAL; | ||
292 | |||
293 | /* Determine the dev_name for the device_node. This code mimics the | ||
294 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
295 | * the dev_name() of the device to match the platform (DMA) device with | ||
296 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
297 | * now). | ||
298 | * | ||
299 | * dai->platform name should already point to an allocated buffer. | ||
300 | */ | ||
301 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
302 | if (ret) | ||
303 | return ret; | ||
304 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
305 | (unsigned long long) res.start, dma_channel_np->name); | ||
306 | |||
307 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
308 | if (!iprop) { | ||
309 | of_node_put(dma_channel_np); | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | |||
313 | *dma_channel_id = *iprop; | ||
314 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
315 | of_node_put(dma_channel_np); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * p1022_ds_probe: platform probe function for the machine driver | ||
322 | * | ||
323 | * Although this is a machine driver, the SSI node is the "master" node with | ||
324 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
325 | * device for each new SSI node that has a codec attached. | ||
326 | */ | ||
327 | static int p1022_ds_probe(struct platform_device *pdev) | ||
328 | { | ||
329 | struct device *dev = pdev->dev.parent; | ||
330 | /* ssi_pdev is the platform device for the SSI node that probed us */ | ||
331 | struct platform_device *ssi_pdev = | ||
332 | container_of(dev, struct platform_device, dev); | ||
333 | struct device_node *np = ssi_pdev->dev.of_node; | ||
334 | struct device_node *codec_np = NULL; | ||
335 | struct platform_device *sound_device = NULL; | ||
336 | struct machine_data *mdata; | ||
337 | int ret = -ENODEV; | ||
338 | const char *sprop; | ||
339 | const u32 *iprop; | ||
340 | |||
341 | /* Find the codec node for this SSI. */ | ||
342 | codec_np = of_parse_phandle(np, "codec-handle", 0); | ||
343 | if (!codec_np) { | ||
344 | dev_err(dev, "could not find codec node\n"); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | |||
348 | mdata = kzalloc(sizeof(struct machine_data), GFP_KERNEL); | ||
349 | if (!mdata) { | ||
350 | ret = -ENOMEM; | ||
351 | goto error_put; | ||
352 | } | ||
353 | |||
354 | mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); | ||
355 | mdata->dai[0].ops = &p1022_ds_ops; | ||
356 | |||
357 | /* Determine the codec name, it will be used as the codec DAI name */ | ||
358 | ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE); | ||
359 | if (ret) { | ||
360 | dev_err(&pdev->dev, "invalid codec node %s\n", | ||
361 | codec_np->full_name); | ||
362 | ret = -EINVAL; | ||
363 | goto error; | ||
364 | } | ||
365 | mdata->dai[0].codec_name = mdata->codec_name; | ||
366 | |||
367 | /* We register two DAIs per SSI, one for playback and the other for | ||
368 | * capture. We support codecs that have separate DAIs for both playback | ||
369 | * and capture. | ||
370 | */ | ||
371 | memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link)); | ||
372 | |||
373 | /* The DAI names from the codec (snd_soc_dai_driver.name) */ | ||
374 | mdata->dai[0].codec_dai_name = "wm8776-hifi-playback"; | ||
375 | mdata->dai[1].codec_dai_name = "wm8776-hifi-capture"; | ||
376 | |||
377 | /* Get the device ID */ | ||
378 | iprop = of_get_property(np, "cell-index", NULL); | ||
379 | if (!iprop) { | ||
380 | dev_err(&pdev->dev, "cell-index property not found\n"); | ||
381 | ret = -EINVAL; | ||
382 | goto error; | ||
383 | } | ||
384 | mdata->ssi_id = *iprop; | ||
385 | |||
386 | /* Get the serial format and clock direction. */ | ||
387 | sprop = of_get_property(np, "fsl,mode", NULL); | ||
388 | if (!sprop) { | ||
389 | dev_err(&pdev->dev, "fsl,mode property not found\n"); | ||
390 | ret = -EINVAL; | ||
391 | goto error; | ||
392 | } | ||
393 | |||
394 | if (strcasecmp(sprop, "i2s-slave") == 0) { | ||
395 | mdata->dai_format = SND_SOC_DAIFMT_I2S; | ||
396 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
397 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
398 | |||
399 | /* In i2s-slave mode, the codec has its own clock source, so we | ||
400 | * need to get the frequency from the device tree and pass it to | ||
401 | * the codec driver. | ||
402 | */ | ||
403 | iprop = of_get_property(codec_np, "clock-frequency", NULL); | ||
404 | if (!iprop || !*iprop) { | ||
405 | dev_err(&pdev->dev, "codec bus-frequency " | ||
406 | "property is missing or invalid\n"); | ||
407 | ret = -EINVAL; | ||
408 | goto error; | ||
409 | } | ||
410 | mdata->clk_frequency = *iprop; | ||
411 | } else if (strcasecmp(sprop, "i2s-master") == 0) { | ||
412 | mdata->dai_format = SND_SOC_DAIFMT_I2S; | ||
413 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
414 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
415 | } else if (strcasecmp(sprop, "lj-slave") == 0) { | ||
416 | mdata->dai_format = SND_SOC_DAIFMT_LEFT_J; | ||
417 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
418 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
419 | } else if (strcasecmp(sprop, "lj-master") == 0) { | ||
420 | mdata->dai_format = SND_SOC_DAIFMT_LEFT_J; | ||
421 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
422 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
423 | } else if (strcasecmp(sprop, "rj-slave") == 0) { | ||
424 | mdata->dai_format = SND_SOC_DAIFMT_RIGHT_J; | ||
425 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
426 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
427 | } else if (strcasecmp(sprop, "rj-master") == 0) { | ||
428 | mdata->dai_format = SND_SOC_DAIFMT_RIGHT_J; | ||
429 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
430 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
431 | } else if (strcasecmp(sprop, "ac97-slave") == 0) { | ||
432 | mdata->dai_format = SND_SOC_DAIFMT_AC97; | ||
433 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
434 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
435 | } else if (strcasecmp(sprop, "ac97-master") == 0) { | ||
436 | mdata->dai_format = SND_SOC_DAIFMT_AC97; | ||
437 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
438 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
439 | } else { | ||
440 | dev_err(&pdev->dev, | ||
441 | "unrecognized fsl,mode property '%s'\n", sprop); | ||
442 | ret = -EINVAL; | ||
443 | goto error; | ||
444 | } | ||
445 | |||
446 | if (!mdata->clk_frequency) { | ||
447 | dev_err(&pdev->dev, "unknown clock frequency\n"); | ||
448 | ret = -EINVAL; | ||
449 | goto error; | ||
450 | } | ||
451 | |||
452 | /* Find the playback DMA channel to use. */ | ||
453 | mdata->dai[0].platform_name = mdata->platform_name[0]; | ||
454 | ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], | ||
455 | &mdata->dma_channel_id[0], | ||
456 | &mdata->dma_id[0]); | ||
457 | if (ret) { | ||
458 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); | ||
459 | goto error; | ||
460 | } | ||
461 | |||
462 | /* Find the capture DMA channel to use. */ | ||
463 | mdata->dai[1].platform_name = mdata->platform_name[1]; | ||
464 | ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], | ||
465 | &mdata->dma_channel_id[1], | ||
466 | &mdata->dma_id[1]); | ||
467 | if (ret) { | ||
468 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); | ||
469 | goto error; | ||
470 | } | ||
471 | |||
472 | /* Initialize our DAI data structure. */ | ||
473 | mdata->dai[0].stream_name = "playback"; | ||
474 | mdata->dai[1].stream_name = "capture"; | ||
475 | mdata->dai[0].name = mdata->dai[0].stream_name; | ||
476 | mdata->dai[1].name = mdata->dai[1].stream_name; | ||
477 | |||
478 | mdata->card.probe = p1022_ds_machine_probe; | ||
479 | mdata->card.remove = p1022_ds_machine_remove; | ||
480 | mdata->card.name = pdev->name; /* The platform driver name */ | ||
481 | mdata->card.num_links = 2; | ||
482 | mdata->card.dai_link = mdata->dai; | ||
483 | |||
484 | /* Allocate a new audio platform device structure */ | ||
485 | sound_device = platform_device_alloc("soc-audio", -1); | ||
486 | if (!sound_device) { | ||
487 | dev_err(&pdev->dev, "platform device alloc failed\n"); | ||
488 | ret = -ENOMEM; | ||
489 | goto error; | ||
490 | } | ||
491 | |||
492 | /* Associate the card data with the sound device */ | ||
493 | platform_set_drvdata(sound_device, &mdata->card); | ||
494 | |||
495 | /* Register with ASoC */ | ||
496 | ret = platform_device_add(sound_device); | ||
497 | if (ret) { | ||
498 | dev_err(&pdev->dev, "platform device add failed\n"); | ||
499 | goto error; | ||
500 | } | ||
501 | |||
502 | of_node_put(codec_np); | ||
503 | |||
504 | return 0; | ||
505 | |||
506 | error: | ||
507 | if (sound_device) | ||
508 | platform_device_unregister(sound_device); | ||
509 | |||
510 | kfree(mdata); | ||
511 | error_put: | ||
512 | of_node_put(codec_np); | ||
513 | return ret; | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * p1022_ds_remove: remove the platform device | ||
518 | * | ||
519 | * This function is called when the platform device is removed. | ||
520 | */ | ||
521 | static int __devexit p1022_ds_remove(struct platform_device *pdev) | ||
522 | { | ||
523 | struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); | ||
524 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
525 | struct machine_data *mdata = | ||
526 | container_of(card, struct machine_data, card); | ||
527 | |||
528 | platform_device_unregister(sound_device); | ||
529 | |||
530 | kfree(mdata); | ||
531 | sound_device->dev.platform_data = NULL; | ||
532 | |||
533 | dev_set_drvdata(&pdev->dev, NULL); | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static struct platform_driver p1022_ds_driver = { | ||
539 | .probe = p1022_ds_probe, | ||
540 | .remove = __devexit_p(p1022_ds_remove), | ||
541 | .driver = { | ||
542 | /* The name must match the 'model' property in the device tree, | ||
543 | * in lowercase letters, but only the part after that last | ||
544 | * comma. This is because some model properties have a "fsl," | ||
545 | * prefix. | ||
546 | */ | ||
547 | .name = "snd-soc-p1022", | ||
548 | .owner = THIS_MODULE, | ||
549 | }, | ||
550 | }; | ||
551 | |||
552 | /** | ||
553 | * p1022_ds_init: machine driver initialization. | ||
554 | * | ||
555 | * This function is called when this module is loaded. | ||
556 | */ | ||
557 | static int __init p1022_ds_init(void) | ||
558 | { | ||
559 | struct device_node *guts_np; | ||
560 | struct resource res; | ||
561 | |||
562 | pr_info("Freescale P1022 DS ALSA SoC machine driver\n"); | ||
563 | |||
564 | /* Get the physical address of the global utilities registers */ | ||
565 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); | ||
566 | if (of_address_to_resource(guts_np, 0, &res)) { | ||
567 | pr_err("p1022-ds: missing/invalid global utilities node\n"); | ||
568 | return -EINVAL; | ||
569 | } | ||
570 | guts_phys = res.start; | ||
571 | of_node_put(guts_np); | ||
572 | |||
573 | return platform_driver_register(&p1022_ds_driver); | ||
574 | } | ||
575 | |||
576 | /** | ||
577 | * p1022_ds_exit: machine driver exit | ||
578 | * | ||
579 | * This function is called when this driver is unloaded. | ||
580 | */ | ||
581 | static void __exit p1022_ds_exit(void) | ||
582 | { | ||
583 | platform_driver_unregister(&p1022_ds_driver); | ||
584 | } | ||
585 | |||
586 | module_init(p1022_ds_init); | ||
587 | module_exit(p1022_ds_exit); | ||
588 | |||
589 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | ||
590 | MODULE_DESCRIPTION("Freescale P1022 DS ALSA SoC machine driver"); | ||
591 | 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); | ||