diff options
Diffstat (limited to 'sound/soc/fsl/mpc8610_hpcd.c')
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 660 |
1 files changed, 311 insertions, 349 deletions
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"); |