diff options
author | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-03-17 16:15:21 -0400 |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-08-12 09:00:00 -0400 |
commit | f0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch) | |
tree | f6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/fsl/mpc8610_hpcd.c | |
parent | bda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff) |
ASoC: multi-component - ASoC Multi-Component Support
This patch extends the ASoC API to allow sound cards to have more than one
CODEC and more than one platform DMA controller. This is achieved by dividing
some current ASoC structures that contain both driver data and device data into
structures that only either contain device data or driver data. i.e.
struct snd_soc_codec ---> struct snd_soc_codec (device data)
+-> struct snd_soc_codec_driver (driver data)
struct snd_soc_platform ---> struct snd_soc_platform (device data)
+-> struct snd_soc_platform_driver (driver data)
struct snd_soc_dai ---> struct snd_soc_dai (device data)
+-> struct snd_soc_dai_driver (driver data)
struct snd_soc_device ---> deleted
This now allows ASoC to be more tightly aligned with the Linux driver model and
also means that every ASoC codec, platform and (platform) DAI is a kernel
device. ASoC component private data is now stored as device private data.
The ASoC sound card struct snd_soc_card has also been updated to store lists
of it's components rather than a pointer to a codec and platform. The PCM
runtime struct soc_pcm_runtime now has pointers to all its components.
This patch adds DAPM support for ASoC multi-component and removes struct
snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec
or runtime PCM level basis rather than using snd_soc_socdev.
Other notable multi-component changes:-
* Stream operations now de-reference less structures.
* close_delayed work() now runs on a DAI basis rather than looping all DAIs
in a card.
* PM suspend()/resume() operations can now handle N CODECs and Platforms
per sound card.
* Added soc_bind_dai_link() to bind the component devices to the sound card.
* Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove
DAI link components.
* sysfs entries can now be registered per component per card.
* snd_soc_new_pcms() functionailty rolled into dai_link_probe().
* snd_soc_register_codec() now does all the codec list and mutex init.
This patch changes the probe() and remove() of the CODEC drivers as follows:-
o Make CODEC driver a platform driver
o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core.
o Removed all static codec pointers (drivers now support > 1 codec dev)
o snd_soc_register_pcms() now done by core.
o snd_soc_register_dai() folded into snd_soc_register_codec().
CS4270 portions:
Acked-by: Timur Tabi <timur@freescale.com>
Some TLV320aic23 and Cirrus platform fixes.
Signed-off-by: Ryan Mallon <ryan@bluewatersys.com>
TI CODEC and OMAP fixes
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Signed-off-by: Jarkko Nikula <jhnikula@gmail.com>
Samsung platform and misc fixes :-
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Jassi Brar <jassi.brar@samsung.com>
Signed-off-by: Seungwhan Youn <sw.youn@samsung.com>
MPC8610 and PPC fixes.
Signed-off-by: Timur Tabi <timur@freescale.com>
i.MX fixes and some core fixes.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
J4740 platform fixes:-
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
CC: Tony Lindgren <tony@atomide.com>
CC: Nicolas Ferre <nicolas.ferre@atmel.com>
CC: Kevin Hilman <khilman@deeprootsystems.com>
CC: Sascha Hauer <s.hauer@pengutronix.de>
CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
CC: Kuninori Morimoto <morimoto.kuninori@renesas.com>
CC: Daniel Gloeckner <dg@emlix.com>
CC: Manuel Lauss <mano@roarinelk.homelinux.net>
CC: Mike Frysinger <vapier.adi@gmail.com>
CC: Arnaud Patard <apatard@mandriva.com>
CC: Wan ZongShun <mcuos.com@gmail.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/fsl/mpc8610_hpcd.c')
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 658 |
1 files changed, 309 insertions, 349 deletions
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 6a2764ee8203..5ba823213abe 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -1,85 +1,96 @@ | |||
1 | /** | 1 | /** |
2 | * Freescale MPC8610HPCD ALSA SoC Fabric driver | 2 | * Freescale MPC8610HPCD ALSA SoC Machine driver |
3 | * | 3 | * |
4 | * Author: Timur Tabi <timur@freescale.com> | 4 | * Author: Timur Tabi <timur@freescale.com> |
5 | * | 5 | * |
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | 6 | * Copyright 2007-2010 Freescale Semiconductor, Inc. |
7 | * under the terms of the GNU General Public License version 2. This | 7 | * |
8 | * program is licensed "as is" without any warranty of any kind, whether | 8 | * This file is licensed under the terms of the GNU General Public License |
9 | * express or implied. | 9 | * version 2. This program is licensed "as is" without any warranty of any |
10 | * kind, whether express or implied. | ||
10 | */ | 11 | */ |
11 | 12 | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
15 | #include <linux/of_device.h> | 15 | #include <linux/of_device.h> |
16 | #include <linux/of_platform.h> | ||
17 | #include <sound/soc.h> | 16 | #include <sound/soc.h> |
18 | #include <asm/immap_86xx.h> | 17 | #include <asm/immap_86xx.h> |
19 | 18 | ||
20 | #include "../codecs/cs4270.h" | ||
21 | #include "fsl_dma.h" | 19 | #include "fsl_dma.h" |
22 | #include "fsl_ssi.h" | 20 | #include "fsl_ssi.h" |
23 | 21 | ||
22 | /* There's only one global utilities register */ | ||
23 | static phys_addr_t guts_phys; | ||
24 | |||
25 | #define DAI_NAME_SIZE 32 | ||
26 | |||
24 | /** | 27 | /** |
25 | * mpc8610_hpcd_data: fabric-specific ASoC device data | 28 | * mpc8610_hpcd_data: machine-specific ASoC device data |
26 | * | 29 | * |
27 | * This structure contains data for a single sound platform device on an | 30 | * This structure contains data for a single sound platform device on an |
28 | * MPC8610 HPCD. Some of the data is taken from the device tree. | 31 | * MPC8610 HPCD. Some of the data is taken from the device tree. |
29 | */ | 32 | */ |
30 | struct mpc8610_hpcd_data { | 33 | struct mpc8610_hpcd_data { |
31 | struct snd_soc_device sound_devdata; | 34 | struct snd_soc_dai_link dai[2]; |
32 | struct snd_soc_dai_link dai; | 35 | struct snd_soc_card card; |
33 | struct snd_soc_card machine; | ||
34 | unsigned int dai_format; | 36 | unsigned int dai_format; |
35 | unsigned int codec_clk_direction; | 37 | unsigned int codec_clk_direction; |
36 | unsigned int cpu_clk_direction; | 38 | unsigned int cpu_clk_direction; |
37 | unsigned int clk_frequency; | 39 | unsigned int clk_frequency; |
38 | struct ccsr_guts __iomem *guts; | 40 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ |
39 | struct ccsr_ssi __iomem *ssi; | 41 | unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ |
40 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ | ||
41 | unsigned int ssi_irq; | ||
42 | unsigned int dma_id; /* 0 = DMA1, 1 = DMA2, etc */ | ||
43 | unsigned int dma_irq[2]; | ||
44 | struct ccsr_dma_channel __iomem *dma[2]; | ||
45 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ | 42 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ |
43 | char codec_dai_name[DAI_NAME_SIZE]; | ||
44 | char codec_name[DAI_NAME_SIZE]; | ||
45 | char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ | ||
46 | }; | 46 | }; |
47 | 47 | ||
48 | /** | 48 | /** |
49 | * mpc8610_hpcd_machine_probe: initalize the board | 49 | * mpc8610_hpcd_machine_probe: initialize the board |
50 | * | 50 | * |
51 | * This function is called when platform_device_add() is called. It is used | 51 | * This function is used to initialize the board-specific hardware. |
52 | * to initialize the board-specific hardware. | ||
53 | * | 52 | * |
54 | * Here we program the DMACR and PMUXCR registers. | 53 | * Here we program the DMACR and PMUXCR registers. |
55 | */ | 54 | */ |
56 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | 55 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) |
57 | { | 56 | { |
57 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
58 | struct mpc8610_hpcd_data *machine_data = | 58 | struct mpc8610_hpcd_data *machine_data = |
59 | sound_device->dev.platform_data; | 59 | container_of(card, struct mpc8610_hpcd_data, card); |
60 | struct ccsr_guts __iomem *guts; | ||
60 | 61 | ||
61 | /* Program the signal routing between the SSI and the DMA */ | 62 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); |
62 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 63 | if (!guts) { |
63 | machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); | 64 | dev_err(card->dev, "could not map global utilities\n"); |
64 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 65 | return -ENOMEM; |
65 | machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); | 66 | } |
66 | 67 | ||
67 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 68 | /* Program the signal routing between the SSI and the DMA */ |
68 | machine_data->dma_channel_id[0], 0); | 69 | guts_set_dmacr(guts, machine_data->dma_id[0], |
69 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | 70 | machine_data->dma_channel_id[0], |
70 | machine_data->dma_channel_id[1], 0); | 71 | CCSR_GUTS_DMACR_DEV_SSI); |
72 | guts_set_dmacr(guts, machine_data->dma_id[1], | ||
73 | machine_data->dma_channel_id[1], | ||
74 | CCSR_GUTS_DMACR_DEV_SSI); | ||
75 | |||
76 | guts_set_pmuxcr_dma(guts, machine_data->dma_id[0], | ||
77 | machine_data->dma_channel_id[0], 0); | ||
78 | guts_set_pmuxcr_dma(guts, machine_data->dma_id[1], | ||
79 | machine_data->dma_channel_id[1], 0); | ||
71 | 80 | ||
72 | switch (machine_data->ssi_id) { | 81 | switch (machine_data->ssi_id) { |
73 | case 0: | 82 | case 0: |
74 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 83 | clrsetbits_be32(&guts->pmuxcr, |
75 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); | 84 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); |
76 | break; | 85 | break; |
77 | case 1: | 86 | case 1: |
78 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 87 | clrsetbits_be32(&guts->pmuxcr, |
79 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); | 88 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); |
80 | break; | 89 | break; |
81 | } | 90 | } |
82 | 91 | ||
92 | iounmap(guts); | ||
93 | |||
83 | return 0; | 94 | return 0; |
84 | } | 95 | } |
85 | 96 | ||
@@ -93,38 +104,15 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | |||
93 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | 104 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) |
94 | { | 105 | { |
95 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 106 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
96 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
97 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
98 | struct mpc8610_hpcd_data *machine_data = | 107 | struct mpc8610_hpcd_data *machine_data = |
99 | rtd->socdev->dev->platform_data; | 108 | container_of(rtd->card, struct mpc8610_hpcd_data, card); |
109 | struct device *dev = rtd->card->dev; | ||
100 | int ret = 0; | 110 | int ret = 0; |
101 | 111 | ||
102 | /* Tell the CPU driver what the serial protocol is. */ | ||
103 | ret = snd_soc_dai_set_fmt(cpu_dai, machine_data->dai_format); | ||
104 | if (ret < 0) { | ||
105 | dev_err(substream->pcm->card->dev, | ||
106 | "could not set CPU driver audio format\n"); | ||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | /* Tell the codec driver what the serial protocol is. */ | 112 | /* Tell the codec driver what the serial protocol is. */ |
111 | ret = snd_soc_dai_set_fmt(codec_dai, machine_data->dai_format); | 113 | ret = snd_soc_dai_set_fmt(rtd->codec_dai, machine_data->dai_format); |
112 | if (ret < 0) { | 114 | if (ret < 0) { |
113 | dev_err(substream->pcm->card->dev, | 115 | dev_err(dev, "could not set codec driver audio format\n"); |
114 | "could not set codec driver audio format\n"); | ||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Tell the CPU driver what the clock frequency is, and whether it's a | ||
120 | * slave or master. | ||
121 | */ | ||
122 | ret = snd_soc_dai_set_sysclk(cpu_dai, 0, | ||
123 | machine_data->clk_frequency, | ||
124 | machine_data->cpu_clk_direction); | ||
125 | if (ret < 0) { | ||
126 | dev_err(substream->pcm->card->dev, | ||
127 | "could not set CPU driver clock parameters\n"); | ||
128 | return ret; | 116 | return ret; |
129 | } | 117 | } |
130 | 118 | ||
@@ -132,12 +120,11 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | |||
132 | * Tell the codec driver what the MCLK frequency is, and whether it's | 120 | * Tell the codec driver what the MCLK frequency is, and whether it's |
133 | * a slave or master. | 121 | * a slave or master. |
134 | */ | 122 | */ |
135 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, | 123 | ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, |
136 | machine_data->clk_frequency, | 124 | machine_data->clk_frequency, |
137 | machine_data->codec_clk_direction); | 125 | machine_data->codec_clk_direction); |
138 | if (ret < 0) { | 126 | if (ret < 0) { |
139 | dev_err(substream->pcm->card->dev, | 127 | dev_err(dev, "could not set codec driver clock params\n"); |
140 | "could not set codec driver clock params\n"); | ||
141 | return ret; | 128 | return ret; |
142 | } | 129 | } |
143 | 130 | ||
@@ -150,116 +137,254 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | |||
150 | * This function is called to remove the sound device for one SSI. We | 137 | * This function is called to remove the sound device for one SSI. We |
151 | * de-program the DMACR and PMUXCR register. | 138 | * de-program the DMACR and PMUXCR register. |
152 | */ | 139 | */ |
153 | int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) | 140 | static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) |
154 | { | 141 | { |
142 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
155 | struct mpc8610_hpcd_data *machine_data = | 143 | struct mpc8610_hpcd_data *machine_data = |
156 | sound_device->dev.platform_data; | 144 | container_of(card, struct mpc8610_hpcd_data, card); |
145 | struct ccsr_guts __iomem *guts; | ||
146 | |||
147 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts)); | ||
148 | if (!guts) { | ||
149 | dev_err(card->dev, "could not map global utilities\n"); | ||
150 | return -ENOMEM; | ||
151 | } | ||
157 | 152 | ||
158 | /* Restore the signal routing */ | 153 | /* Restore the signal routing */ |
159 | 154 | ||
160 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 155 | guts_set_dmacr(guts, machine_data->dma_id[0], |
161 | machine_data->dma_channel_id[0], 0); | 156 | machine_data->dma_channel_id[0], 0); |
162 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 157 | guts_set_dmacr(guts, machine_data->dma_id[1], |
163 | machine_data->dma_channel_id[1], 0); | 158 | machine_data->dma_channel_id[1], 0); |
164 | 159 | ||
165 | switch (machine_data->ssi_id) { | 160 | switch (machine_data->ssi_id) { |
166 | case 0: | 161 | case 0: |
167 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 162 | clrsetbits_be32(&guts->pmuxcr, |
168 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); | 163 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); |
169 | break; | 164 | break; |
170 | case 1: | 165 | case 1: |
171 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 166 | clrsetbits_be32(&guts->pmuxcr, |
172 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); | 167 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); |
173 | break; | 168 | break; |
174 | } | 169 | } |
175 | 170 | ||
171 | iounmap(guts); | ||
172 | |||
176 | return 0; | 173 | return 0; |
177 | } | 174 | } |
178 | 175 | ||
179 | /** | 176 | /** |
180 | * mpc8610_hpcd_ops: ASoC fabric driver operations | 177 | * mpc8610_hpcd_ops: ASoC machine driver operations |
181 | */ | 178 | */ |
182 | static struct snd_soc_ops mpc8610_hpcd_ops = { | 179 | static struct snd_soc_ops mpc8610_hpcd_ops = { |
183 | .startup = mpc8610_hpcd_startup, | 180 | .startup = mpc8610_hpcd_startup, |
184 | }; | 181 | }; |
185 | 182 | ||
186 | /** | 183 | /** |
187 | * mpc8610_hpcd_probe: OF probe function for the fabric driver | 184 | * get_node_by_phandle_name - get a node by its phandle name |
188 | * | 185 | * |
189 | * This function gets called when an SSI node is found in the device tree. | 186 | * This function takes a node, the name of a property in that node, and a |
187 | * compatible string. Assuming the property is a phandle to another node, | ||
188 | * it returns that node, (optionally) if that node is compatible. | ||
190 | * | 189 | * |
191 | * Although this is a fabric driver, the SSI node is the "master" node with | 190 | * If the property is not a phandle, or the node it points to is not compatible |
192 | * respect to audio hardware connections. Therefore, we create a new ASoC | 191 | * with the specific string, then NULL is returned. |
193 | * device for each new SSI node that has a codec attached. | 192 | */ |
193 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
194 | const char *name, | ||
195 | const char *compatible) | ||
196 | { | ||
197 | const phandle *ph; | ||
198 | int len; | ||
199 | |||
200 | ph = of_get_property(np, name, &len); | ||
201 | if (!ph || (len != sizeof(phandle))) | ||
202 | return NULL; | ||
203 | |||
204 | np = of_find_node_by_phandle(*ph); | ||
205 | if (!np) | ||
206 | return NULL; | ||
207 | |||
208 | if (compatible && !of_device_is_compatible(np, compatible)) { | ||
209 | of_node_put(np); | ||
210 | return NULL; | ||
211 | } | ||
212 | |||
213 | return np; | ||
214 | } | ||
215 | |||
216 | /** | ||
217 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
194 | * | 218 | * |
195 | * FIXME: Currently, we only support one DMA controller, so if there are | 219 | * Return the value of the cell-index property of the parent of the given |
196 | * multiple SSI nodes with codecs, only the first will be supported. | 220 | * node. This is used for DMA channel nodes that need to know the DMA ID |
221 | * of the controller they are on. | ||
222 | */ | ||
223 | static int get_parent_cell_index(struct device_node *np) | ||
224 | { | ||
225 | struct device_node *parent = of_get_parent(np); | ||
226 | const u32 *iprop; | ||
227 | |||
228 | if (!parent) | ||
229 | return -1; | ||
230 | |||
231 | iprop = of_get_property(parent, "cell-index", NULL); | ||
232 | of_node_put(parent); | ||
233 | |||
234 | if (!iprop) | ||
235 | return -1; | ||
236 | |||
237 | return *iprop; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * codec_node_dev_name - determine the dev_name for a codec node | ||
197 | * | 242 | * |
198 | * FIXME: Even if we did support multiple DMA controllers, we have no | 243 | * This function determines the dev_name for an I2C node. This is the name |
199 | * mechanism for assigning DMA controllers and channels to the individual | 244 | * that would be returned by dev_name() if this device_node were part of a |
200 | * SSI devices. We also probably aren't compatible with the generic Elo DMA | 245 | * 'struct device' It's ugly and hackish, but it works. |
201 | * device driver. | 246 | * |
247 | * The dev_name for such devices include the bus number and I2C address. For | ||
248 | * example, "cs4270-codec.0-004f". | ||
202 | */ | 249 | */ |
203 | static int mpc8610_hpcd_probe(struct of_device *ofdev, | 250 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) |
204 | const struct of_device_id *match) | ||
205 | { | 251 | { |
206 | struct device_node *np = ofdev->dev.of_node; | ||
207 | struct device_node *codec_np = NULL; | ||
208 | struct device_node *guts_np = NULL; | ||
209 | struct device_node *dma_np = NULL; | ||
210 | struct device_node *dma_channel_np = NULL; | ||
211 | const phandle *codec_ph; | ||
212 | const char *sprop; | ||
213 | const u32 *iprop; | 252 | const u32 *iprop; |
253 | int bus, addr; | ||
254 | char temp[DAI_NAME_SIZE]; | ||
255 | |||
256 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
257 | |||
258 | iprop = of_get_property(np, "reg", NULL); | ||
259 | if (!iprop) | ||
260 | return -EINVAL; | ||
261 | |||
262 | addr = *iprop; | ||
263 | |||
264 | bus = get_parent_cell_index(np); | ||
265 | if (bus < 0) | ||
266 | return bus; | ||
267 | |||
268 | snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int get_dma_channel(struct device_node *ssi_np, | ||
274 | const char *compatible, | ||
275 | struct snd_soc_dai_link *dai, | ||
276 | unsigned int *dma_channel_id, | ||
277 | unsigned int *dma_id) | ||
278 | { | ||
214 | struct resource res; | 279 | struct resource res; |
280 | struct device_node *dma_channel_np; | ||
281 | const u32 *iprop; | ||
282 | int ret; | ||
283 | |||
284 | dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, | ||
285 | "fsl,ssi-dma-channel"); | ||
286 | if (!dma_channel_np) | ||
287 | return -EINVAL; | ||
288 | |||
289 | /* Determine the dev_name for the device_node. This code mimics the | ||
290 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
291 | * the dev_name() of the device to match the platform (DMA) device with | ||
292 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
293 | * now). | ||
294 | * | ||
295 | * dai->platform name should already point to an allocated buffer. | ||
296 | */ | ||
297 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
298 | if (ret) | ||
299 | return ret; | ||
300 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
301 | (unsigned long long) res.start, dma_channel_np->name); | ||
302 | |||
303 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
304 | if (!iprop) { | ||
305 | of_node_put(dma_channel_np); | ||
306 | return -EINVAL; | ||
307 | } | ||
308 | |||
309 | *dma_channel_id = *iprop; | ||
310 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
311 | of_node_put(dma_channel_np); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * mpc8610_hpcd_probe: platform probe function for the machine driver | ||
318 | * | ||
319 | * Although this is a machine driver, the SSI node is the "master" node with | ||
320 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
321 | * device for each new SSI node that has a codec attached. | ||
322 | */ | ||
323 | static int mpc8610_hpcd_probe(struct platform_device *pdev) | ||
324 | { | ||
325 | struct device *dev = pdev->dev.parent; | ||
326 | /* of_dev is the OF device for the SSI node that probed us */ | ||
327 | struct of_device *of_dev = container_of(dev, struct of_device, dev); | ||
328 | struct device_node *np = of_dev->dev.of_node; | ||
329 | struct device_node *codec_np = NULL; | ||
215 | struct platform_device *sound_device = NULL; | 330 | struct platform_device *sound_device = NULL; |
216 | struct mpc8610_hpcd_data *machine_data; | 331 | struct mpc8610_hpcd_data *machine_data; |
217 | struct fsl_ssi_info ssi_info; | ||
218 | struct fsl_dma_info dma_info; | ||
219 | int ret = -ENODEV; | 332 | int ret = -ENODEV; |
220 | unsigned int playback_dma_channel; | 333 | const char *sprop; |
221 | unsigned int capture_dma_channel; | 334 | const u32 *iprop; |
335 | |||
336 | /* We are only interested in SSIs with a codec phandle in them, | ||
337 | * so let's make sure this SSI has one. The MPC8610 HPCD only | ||
338 | * knows about the CS4270 codec, so reject anything else. | ||
339 | */ | ||
340 | codec_np = get_node_by_phandle_name(np, "codec-handle", | ||
341 | "cirrus,cs4270"); | ||
342 | if (!codec_np) { | ||
343 | dev_err(dev, "invalid codec node\n"); | ||
344 | return -EINVAL; | ||
345 | } | ||
222 | 346 | ||
223 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); | 347 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); |
224 | if (!machine_data) | 348 | if (!machine_data) |
225 | return -ENOMEM; | 349 | return -ENOMEM; |
226 | 350 | ||
227 | memset(&ssi_info, 0, sizeof(ssi_info)); | 351 | machine_data->dai[0].cpu_dai_name = dev_name(&of_dev->dev); |
228 | memset(&dma_info, 0, sizeof(dma_info)); | 352 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; |
229 | 353 | ||
230 | ssi_info.dev = &ofdev->dev; | 354 | /* Determine the codec name, it will be used as the codec DAI name */ |
231 | 355 | ret = codec_node_dev_name(codec_np, machine_data->codec_name, | |
232 | /* | 356 | DAI_NAME_SIZE); |
233 | * We are only interested in SSIs with a codec phandle in them, so let's | 357 | if (ret) { |
234 | * make sure this SSI has one. | 358 | dev_err(&pdev->dev, "invalid codec node %s\n", |
235 | */ | 359 | codec_np->full_name); |
236 | codec_ph = of_get_property(np, "codec-handle", NULL); | 360 | ret = -EINVAL; |
237 | if (!codec_ph) | ||
238 | goto error; | 361 | goto error; |
362 | } | ||
363 | machine_data->dai[0].codec_name = machine_data->codec_name; | ||
239 | 364 | ||
240 | codec_np = of_find_node_by_phandle(*codec_ph); | 365 | /* The DAI name from the codec (snd_soc_dai_driver.name) */ |
241 | if (!codec_np) | 366 | machine_data->dai[0].codec_dai_name = "cs4270-hifi"; |
242 | goto error; | ||
243 | 367 | ||
244 | /* The MPC8610 HPCD only knows about the CS4270 codec, so reject | 368 | /* We register two DAIs per SSI, one for playback and the other for |
245 | anything else. */ | 369 | * capture. Currently, we only support codecs that have one DAI for |
246 | if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) | 370 | * both playback and capture. |
247 | goto error; | 371 | */ |
372 | memcpy(&machine_data->dai[1], &machine_data->dai[0], | ||
373 | sizeof(struct snd_soc_dai_link)); | ||
248 | 374 | ||
249 | /* Get the device ID */ | 375 | /* Get the device ID */ |
250 | iprop = of_get_property(np, "cell-index", NULL); | 376 | iprop = of_get_property(np, "cell-index", NULL); |
251 | if (!iprop) { | 377 | if (!iprop) { |
252 | dev_err(&ofdev->dev, "cell-index property not found\n"); | 378 | dev_err(&pdev->dev, "cell-index property not found\n"); |
253 | ret = -EINVAL; | 379 | ret = -EINVAL; |
254 | goto error; | 380 | goto error; |
255 | } | 381 | } |
256 | machine_data->ssi_id = *iprop; | 382 | machine_data->ssi_id = *iprop; |
257 | ssi_info.id = *iprop; | ||
258 | 383 | ||
259 | /* Get the serial format and clock direction. */ | 384 | /* Get the serial format and clock direction. */ |
260 | sprop = of_get_property(np, "fsl,mode", NULL); | 385 | sprop = of_get_property(np, "fsl,mode", NULL); |
261 | if (!sprop) { | 386 | if (!sprop) { |
262 | dev_err(&ofdev->dev, "fsl,mode property not found\n"); | 387 | dev_err(&pdev->dev, "fsl,mode property not found\n"); |
263 | ret = -EINVAL; | 388 | ret = -EINVAL; |
264 | goto error; | 389 | goto error; |
265 | } | 390 | } |
@@ -269,15 +394,14 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
269 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | 394 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; |
270 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | 395 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; |
271 | 396 | ||
272 | /* | 397 | /* In i2s-slave mode, the codec has its own clock source, so we |
273 | * In i2s-slave mode, the codec has its own clock source, so we | ||
274 | * need to get the frequency from the device tree and pass it to | 398 | * need to get the frequency from the device tree and pass it to |
275 | * the codec driver. | 399 | * the codec driver. |
276 | */ | 400 | */ |
277 | iprop = of_get_property(codec_np, "clock-frequency", NULL); | 401 | iprop = of_get_property(codec_np, "clock-frequency", NULL); |
278 | if (!iprop || !*iprop) { | 402 | if (!iprop || !*iprop) { |
279 | dev_err(&ofdev->dev, "codec bus-frequency property " | 403 | dev_err(&pdev->dev, "codec bus-frequency " |
280 | "is missing or invalid\n"); | 404 | "property is missing or invalid\n"); |
281 | ret = -EINVAL; | 405 | ret = -EINVAL; |
282 | goto error; | 406 | goto error; |
283 | } | 407 | } |
@@ -311,317 +435,153 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev, | |||
311 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | 435 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; |
312 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | 436 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; |
313 | } else { | 437 | } else { |
314 | dev_err(&ofdev->dev, | 438 | dev_err(&pdev->dev, |
315 | "unrecognized fsl,mode property \"%s\"\n", sprop); | 439 | "unrecognized fsl,mode property '%s'\n", sprop); |
316 | ret = -EINVAL; | 440 | ret = -EINVAL; |
317 | goto error; | 441 | goto error; |
318 | } | 442 | } |
319 | 443 | ||
320 | if (!machine_data->clk_frequency) { | 444 | if (!machine_data->clk_frequency) { |
321 | dev_err(&ofdev->dev, "unknown clock frequency\n"); | 445 | dev_err(&pdev->dev, "unknown clock frequency\n"); |
322 | ret = -EINVAL; | 446 | ret = -EINVAL; |
323 | goto error; | 447 | goto error; |
324 | } | 448 | } |
325 | 449 | ||
326 | /* Read the SSI information from the device tree */ | 450 | /* Find the playback DMA channel to use. */ |
327 | ret = of_address_to_resource(np, 0, &res); | 451 | machine_data->dai[0].platform_name = machine_data->platform_name[0]; |
452 | ret = get_dma_channel(np, "fsl,playback-dma", &machine_data->dai[0], | ||
453 | &machine_data->dma_channel_id[0], | ||
454 | &machine_data->dma_id[0]); | ||
328 | if (ret) { | 455 | if (ret) { |
329 | dev_err(&ofdev->dev, "could not obtain SSI address\n"); | 456 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); |
330 | goto error; | ||
331 | } | ||
332 | if (!res.start) { | ||
333 | dev_err(&ofdev->dev, "invalid SSI address\n"); | ||
334 | goto error; | ||
335 | } | ||
336 | ssi_info.ssi_phys = res.start; | ||
337 | |||
338 | machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi)); | ||
339 | if (!machine_data->ssi) { | ||
340 | dev_err(&ofdev->dev, "could not map SSI address %x\n", | ||
341 | ssi_info.ssi_phys); | ||
342 | ret = -EINVAL; | ||
343 | goto error; | ||
344 | } | ||
345 | ssi_info.ssi = machine_data->ssi; | ||
346 | |||
347 | |||
348 | /* Get the IRQ of the SSI */ | ||
349 | machine_data->ssi_irq = irq_of_parse_and_map(np, 0); | ||
350 | if (!machine_data->ssi_irq) { | ||
351 | dev_err(&ofdev->dev, "could not get SSI IRQ\n"); | ||
352 | ret = -EINVAL; | ||
353 | goto error; | ||
354 | } | ||
355 | ssi_info.irq = machine_data->ssi_irq; | ||
356 | |||
357 | /* Do we want to use asynchronous mode? */ | ||
358 | ssi_info.asynchronous = | ||
359 | of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0; | ||
360 | if (ssi_info.asynchronous) | ||
361 | dev_info(&ofdev->dev, "using asynchronous mode\n"); | ||
362 | |||
363 | /* Map the global utilities registers. */ | ||
364 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); | ||
365 | if (!guts_np) { | ||
366 | dev_err(&ofdev->dev, "could not obtain address of GUTS\n"); | ||
367 | ret = -EINVAL; | ||
368 | goto error; | ||
369 | } | ||
370 | machine_data->guts = of_iomap(guts_np, 0); | ||
371 | of_node_put(guts_np); | ||
372 | if (!machine_data->guts) { | ||
373 | dev_err(&ofdev->dev, "could not map GUTS\n"); | ||
374 | ret = -EINVAL; | ||
375 | goto error; | ||
376 | } | ||
377 | |||
378 | /* Find the DMA channels to use. Both SSIs need to use the same DMA | ||
379 | * controller, so let's use DMA#1. | ||
380 | */ | ||
381 | for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { | ||
382 | iprop = of_get_property(dma_np, "cell-index", NULL); | ||
383 | if (iprop && (*iprop == 0)) { | ||
384 | of_node_put(dma_np); | ||
385 | break; | ||
386 | } | ||
387 | } | ||
388 | if (!dma_np) { | ||
389 | dev_err(&ofdev->dev, "could not find DMA node\n"); | ||
390 | ret = -EINVAL; | ||
391 | goto error; | ||
392 | } | ||
393 | machine_data->dma_id = *iprop; | ||
394 | |||
395 | /* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA | ||
396 | * channels 2 and 3. This is just how the MPC8610 is wired | ||
397 | * internally. | ||
398 | */ | ||
399 | playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2; | ||
400 | capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3; | ||
401 | |||
402 | /* | ||
403 | * Find the DMA channels to use. | ||
404 | */ | ||
405 | while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { | ||
406 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
407 | if (iprop && (*iprop == playback_dma_channel)) { | ||
408 | /* dma_channel[0] and dma_irq[0] are for playback */ | ||
409 | dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); | ||
410 | dma_info.dma_irq[0] = | ||
411 | irq_of_parse_and_map(dma_channel_np, 0); | ||
412 | machine_data->dma_channel_id[0] = *iprop; | ||
413 | continue; | ||
414 | } | ||
415 | if (iprop && (*iprop == capture_dma_channel)) { | ||
416 | /* dma_channel[1] and dma_irq[1] are for capture */ | ||
417 | dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); | ||
418 | dma_info.dma_irq[1] = | ||
419 | irq_of_parse_and_map(dma_channel_np, 0); | ||
420 | machine_data->dma_channel_id[1] = *iprop; | ||
421 | continue; | ||
422 | } | ||
423 | } | ||
424 | if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] || | ||
425 | !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) { | ||
426 | dev_err(&ofdev->dev, "could not find DMA channels\n"); | ||
427 | ret = -EINVAL; | ||
428 | goto error; | 457 | goto error; |
429 | } | 458 | } |
430 | 459 | ||
431 | dma_info.ssi_stx_phys = ssi_info.ssi_phys + | 460 | /* Find the capture DMA channel to use. */ |
432 | offsetof(struct ccsr_ssi, stx0); | 461 | machine_data->dai[1].platform_name = machine_data->platform_name[1]; |
433 | dma_info.ssi_srx_phys = ssi_info.ssi_phys + | 462 | ret = get_dma_channel(np, "fsl,capture-dma", &machine_data->dai[1], |
434 | offsetof(struct ccsr_ssi, srx0); | 463 | &machine_data->dma_channel_id[1], |
435 | 464 | &machine_data->dma_id[1]); | |
436 | /* We have the DMA information, so tell the DMA driver what it is */ | 465 | if (ret) { |
437 | if (!fsl_dma_configure(&dma_info)) { | 466 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); |
438 | dev_err(&ofdev->dev, "could not instantiate DMA device\n"); | ||
439 | ret = -EBUSY; | ||
440 | goto error; | 467 | goto error; |
441 | } | 468 | } |
442 | 469 | ||
443 | /* | 470 | /* Initialize our DAI data structure. */ |
444 | * Initialize our DAI data structure. We should probably get this | 471 | machine_data->dai[0].stream_name = "playback"; |
445 | * information from the device tree. | 472 | machine_data->dai[1].stream_name = "capture"; |
446 | */ | 473 | machine_data->dai[0].name = machine_data->dai[0].stream_name; |
447 | machine_data->dai.name = "CS4270"; | 474 | machine_data->dai[1].name = machine_data->dai[1].stream_name; |
448 | machine_data->dai.stream_name = "CS4270"; | ||
449 | |||
450 | machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info); | ||
451 | machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */ | ||
452 | machine_data->dai.ops = &mpc8610_hpcd_ops; | ||
453 | 475 | ||
454 | machine_data->machine.probe = mpc8610_hpcd_machine_probe; | 476 | machine_data->card.probe = mpc8610_hpcd_machine_probe; |
455 | machine_data->machine.remove = mpc8610_hpcd_machine_remove; | 477 | machine_data->card.remove = mpc8610_hpcd_machine_remove; |
456 | machine_data->machine.name = "MPC8610 HPCD"; | 478 | machine_data->card.name = pdev->name; /* The platform driver name */ |
457 | machine_data->machine.num_links = 1; | 479 | machine_data->card.num_links = 2; |
458 | machine_data->machine.dai_link = &machine_data->dai; | 480 | machine_data->card.dai_link = machine_data->dai; |
459 | 481 | ||
460 | /* Allocate a new audio platform device structure */ | 482 | /* Allocate a new audio platform device structure */ |
461 | sound_device = platform_device_alloc("soc-audio", -1); | 483 | sound_device = platform_device_alloc("soc-audio", -1); |
462 | if (!sound_device) { | 484 | if (!sound_device) { |
463 | dev_err(&ofdev->dev, "platform device allocation failed\n"); | 485 | dev_err(&pdev->dev, "platform device alloc failed\n"); |
464 | ret = -ENOMEM; | 486 | ret = -ENOMEM; |
465 | goto error; | 487 | goto error; |
466 | } | 488 | } |
467 | 489 | ||
468 | machine_data->sound_devdata.card = &machine_data->machine; | 490 | /* Associate the card data with the sound device */ |
469 | machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; | 491 | platform_set_drvdata(sound_device, &machine_data->card); |
470 | machine_data->machine.platform = &fsl_soc_platform; | ||
471 | |||
472 | sound_device->dev.platform_data = machine_data; | ||
473 | |||
474 | 492 | ||
475 | /* Set the platform device and ASoC device to point to each other */ | 493 | /* Register with ASoC */ |
476 | platform_set_drvdata(sound_device, &machine_data->sound_devdata); | ||
477 | |||
478 | machine_data->sound_devdata.dev = &sound_device->dev; | ||
479 | |||
480 | |||
481 | /* Tell ASoC to probe us. This will call mpc8610_hpcd_machine.probe(), | ||
482 | if it exists. */ | ||
483 | ret = platform_device_add(sound_device); | 494 | ret = platform_device_add(sound_device); |
484 | |||
485 | if (ret) { | 495 | if (ret) { |
486 | dev_err(&ofdev->dev, "platform device add failed\n"); | 496 | dev_err(&pdev->dev, "platform device add failed\n"); |
487 | goto error; | 497 | goto error; |
488 | } | 498 | } |
489 | 499 | ||
490 | dev_set_drvdata(&ofdev->dev, sound_device); | 500 | of_node_put(codec_np); |
491 | 501 | ||
492 | return 0; | 502 | return 0; |
493 | 503 | ||
494 | error: | 504 | error: |
495 | of_node_put(codec_np); | 505 | of_node_put(codec_np); |
496 | of_node_put(guts_np); | ||
497 | of_node_put(dma_np); | ||
498 | of_node_put(dma_channel_np); | ||
499 | 506 | ||
500 | if (sound_device) | 507 | if (sound_device) |
501 | platform_device_unregister(sound_device); | 508 | platform_device_unregister(sound_device); |
502 | 509 | ||
503 | if (machine_data->dai.cpu_dai) | ||
504 | fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); | ||
505 | |||
506 | if (ssi_info.ssi) | ||
507 | iounmap(ssi_info.ssi); | ||
508 | |||
509 | if (ssi_info.irq) | ||
510 | irq_dispose_mapping(ssi_info.irq); | ||
511 | |||
512 | if (dma_info.dma_channel[0]) | ||
513 | iounmap(dma_info.dma_channel[0]); | ||
514 | |||
515 | if (dma_info.dma_channel[1]) | ||
516 | iounmap(dma_info.dma_channel[1]); | ||
517 | |||
518 | if (dma_info.dma_irq[0]) | ||
519 | irq_dispose_mapping(dma_info.dma_irq[0]); | ||
520 | |||
521 | if (dma_info.dma_irq[1]) | ||
522 | irq_dispose_mapping(dma_info.dma_irq[1]); | ||
523 | |||
524 | if (machine_data->guts) | ||
525 | iounmap(machine_data->guts); | ||
526 | |||
527 | kfree(machine_data); | 510 | kfree(machine_data); |
528 | 511 | ||
529 | return ret; | 512 | return ret; |
530 | } | 513 | } |
531 | 514 | ||
532 | /** | 515 | /** |
533 | * mpc8610_hpcd_remove: remove the OF device | 516 | * mpc8610_hpcd_remove: remove the platform device |
534 | * | 517 | * |
535 | * This function is called when the OF device is removed. | 518 | * This function is called when the platform device is removed. |
536 | */ | 519 | */ |
537 | static int mpc8610_hpcd_remove(struct of_device *ofdev) | 520 | static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev) |
538 | { | 521 | { |
539 | struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); | 522 | struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); |
523 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
540 | struct mpc8610_hpcd_data *machine_data = | 524 | struct mpc8610_hpcd_data *machine_data = |
541 | sound_device->dev.platform_data; | 525 | container_of(card, struct mpc8610_hpcd_data, card); |
542 | 526 | ||
543 | platform_device_unregister(sound_device); | 527 | platform_device_unregister(sound_device); |
544 | 528 | ||
545 | if (machine_data->dai.cpu_dai) | ||
546 | fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); | ||
547 | |||
548 | if (machine_data->ssi) | ||
549 | iounmap(machine_data->ssi); | ||
550 | |||
551 | if (machine_data->dma[0]) | ||
552 | iounmap(machine_data->dma[0]); | ||
553 | |||
554 | if (machine_data->dma[1]) | ||
555 | iounmap(machine_data->dma[1]); | ||
556 | |||
557 | if (machine_data->dma_irq[0]) | ||
558 | irq_dispose_mapping(machine_data->dma_irq[0]); | ||
559 | |||
560 | if (machine_data->dma_irq[1]) | ||
561 | irq_dispose_mapping(machine_data->dma_irq[1]); | ||
562 | |||
563 | if (machine_data->guts) | ||
564 | iounmap(machine_data->guts); | ||
565 | |||
566 | kfree(machine_data); | 529 | kfree(machine_data); |
567 | sound_device->dev.platform_data = NULL; | 530 | sound_device->dev.platform_data = NULL; |
568 | 531 | ||
569 | dev_set_drvdata(&ofdev->dev, NULL); | 532 | dev_set_drvdata(&pdev->dev, NULL); |
570 | 533 | ||
571 | return 0; | 534 | return 0; |
572 | } | 535 | } |
573 | 536 | ||
574 | static struct of_device_id mpc8610_hpcd_match[] = { | 537 | static struct platform_driver mpc8610_hpcd_driver = { |
575 | { | 538 | .probe = mpc8610_hpcd_probe, |
576 | .compatible = "fsl,mpc8610-ssi", | 539 | .remove = __devexit_p(mpc8610_hpcd_remove), |
577 | }, | ||
578 | {} | ||
579 | }; | ||
580 | MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match); | ||
581 | |||
582 | static struct of_platform_driver mpc8610_hpcd_of_driver = { | ||
583 | .driver = { | 540 | .driver = { |
584 | .name = "mpc8610_hpcd", | 541 | /* The name must match the 'model' property in the device tree, |
542 | * in lowercase letters. | ||
543 | */ | ||
544 | .name = "snd-soc-mpc8610hpcd", | ||
585 | .owner = THIS_MODULE, | 545 | .owner = THIS_MODULE, |
586 | .of_match_table = mpc8610_hpcd_match, | ||
587 | }, | 546 | }, |
588 | .probe = mpc8610_hpcd_probe, | ||
589 | .remove = mpc8610_hpcd_remove, | ||
590 | }; | 547 | }; |
591 | 548 | ||
592 | /** | 549 | /** |
593 | * mpc8610_hpcd_init: fabric driver initialization. | 550 | * mpc8610_hpcd_init: machine driver initialization. |
594 | * | 551 | * |
595 | * This function is called when this module is loaded. | 552 | * This function is called when this module is loaded. |
596 | */ | 553 | */ |
597 | static int __init mpc8610_hpcd_init(void) | 554 | static int __init mpc8610_hpcd_init(void) |
598 | { | 555 | { |
599 | int ret; | 556 | struct device_node *guts_np; |
600 | 557 | struct resource res; | |
601 | printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); | ||
602 | 558 | ||
603 | ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); | 559 | pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); |
604 | 560 | ||
605 | if (ret) | 561 | /* Get the physical address of the global utilities registers */ |
606 | printk(KERN_ERR | 562 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); |
607 | "mpc8610-hpcd: failed to register platform driver\n"); | 563 | if (of_address_to_resource(guts_np, 0, &res)) { |
564 | pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); | ||
565 | return -EINVAL; | ||
566 | } | ||
567 | guts_phys = res.start; | ||
608 | 568 | ||
609 | return ret; | 569 | return platform_driver_register(&mpc8610_hpcd_driver); |
610 | } | 570 | } |
611 | 571 | ||
612 | /** | 572 | /** |
613 | * mpc8610_hpcd_exit: fabric driver exit | 573 | * mpc8610_hpcd_exit: machine driver exit |
614 | * | 574 | * |
615 | * This function is called when this driver is unloaded. | 575 | * This function is called when this driver is unloaded. |
616 | */ | 576 | */ |
617 | static void __exit mpc8610_hpcd_exit(void) | 577 | static void __exit mpc8610_hpcd_exit(void) |
618 | { | 578 | { |
619 | of_unregister_platform_driver(&mpc8610_hpcd_of_driver); | 579 | platform_driver_unregister(&mpc8610_hpcd_driver); |
620 | } | 580 | } |
621 | 581 | ||
622 | module_init(mpc8610_hpcd_init); | 582 | module_init(mpc8610_hpcd_init); |
623 | module_exit(mpc8610_hpcd_exit); | 583 | module_exit(mpc8610_hpcd_exit); |
624 | 584 | ||
625 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 585 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
626 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); | 586 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); |
627 | MODULE_LICENSE("GPL"); | 587 | MODULE_LICENSE("GPL v2"); |