diff options
Diffstat (limited to 'sound/soc/fsl/mpc8610_hpcd.c')
-rw-r--r-- | sound/soc/fsl/mpc8610_hpcd.c | 661 |
1 files changed, 311 insertions, 350 deletions
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 3b13b8d65262..c16c6b2eff95 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> | 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 snd_soc_card *card) |
57 | { | 57 | { |
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_86xx __iomem *guts; | ||
60 | 61 | ||
61 | /* Program the signal routing between the SSI and the DMA */ | 62 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); |
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 snd_soc_card *card) |
154 | { | 141 | { |
155 | struct mpc8610_hpcd_data *machine_data = | 142 | struct mpc8610_hpcd_data *machine_data = |
156 | sound_device->dev.platform_data; | 143 | container_of(card, struct mpc8610_hpcd_data, card); |
144 | struct ccsr_guts_86xx __iomem *guts; | ||
145 | |||
146 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_86xx)); | ||
147 | if (!guts) { | ||
148 | dev_err(card->dev, "could not map global utilities\n"); | ||
149 | return -ENOMEM; | ||
150 | } | ||
157 | 151 | ||
158 | /* Restore the signal routing */ | 152 | /* Restore the signal routing */ |
159 | 153 | ||
160 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 154 | guts_set_dmacr(guts, machine_data->dma_id[0], |
161 | machine_data->dma_channel_id[0], 0); | 155 | machine_data->dma_channel_id[0], 0); |
162 | guts_set_dmacr(machine_data->guts, machine_data->dma_id, | 156 | guts_set_dmacr(guts, machine_data->dma_id[1], |
163 | machine_data->dma_channel_id[1], 0); | 157 | machine_data->dma_channel_id[1], 0); |
164 | 158 | ||
165 | switch (machine_data->ssi_id) { | 159 | switch (machine_data->ssi_id) { |
166 | case 0: | 160 | case 0: |
167 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 161 | clrsetbits_be32(&guts->pmuxcr, |
168 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); | 162 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); |
169 | break; | 163 | break; |
170 | case 1: | 164 | case 1: |
171 | clrsetbits_be32(&machine_data->guts->pmuxcr, | 165 | clrsetbits_be32(&guts->pmuxcr, |
172 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); | 166 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_LA); |
173 | break; | 167 | break; |
174 | } | 168 | } |
175 | 169 | ||
170 | iounmap(guts); | ||
171 | |||
176 | return 0; | 172 | return 0; |
177 | } | 173 | } |
178 | 174 | ||
179 | /** | 175 | /** |
180 | * mpc8610_hpcd_ops: ASoC fabric driver operations | 176 | * mpc8610_hpcd_ops: ASoC machine driver operations |
181 | */ | 177 | */ |
182 | static struct snd_soc_ops mpc8610_hpcd_ops = { | 178 | static struct snd_soc_ops mpc8610_hpcd_ops = { |
183 | .startup = mpc8610_hpcd_startup, | 179 | .startup = mpc8610_hpcd_startup, |
184 | }; | 180 | }; |
185 | 181 | ||
186 | /** | 182 | /** |
187 | * mpc8610_hpcd_probe: OF probe function for the fabric driver | 183 | * get_node_by_phandle_name - get a node by its phandle name |
188 | * | 184 | * |
189 | * This function gets called when an SSI node is found in the device tree. | 185 | * This function takes a node, the name of a property in that node, and a |
186 | * compatible string. Assuming the property is a phandle to another node, | ||
187 | * it returns that node, (optionally) if that node is compatible. | ||
190 | * | 188 | * |
191 | * Although this is a fabric driver, the SSI node is the "master" node with | 189 | * 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 | 190 | * with the specific string, then NULL is returned. |
193 | * device for each new SSI node that has a codec attached. | 191 | */ |
192 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
193 | const char *name, | ||
194 | const char *compatible) | ||
195 | { | ||
196 | const phandle *ph; | ||
197 | int len; | ||
198 | |||
199 | ph = of_get_property(np, name, &len); | ||
200 | if (!ph || (len != sizeof(phandle))) | ||
201 | return NULL; | ||
202 | |||
203 | np = of_find_node_by_phandle(*ph); | ||
204 | if (!np) | ||
205 | return NULL; | ||
206 | |||
207 | if (compatible && !of_device_is_compatible(np, compatible)) { | ||
208 | of_node_put(np); | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
212 | return np; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
217 | * | ||
218 | * Return the value of the cell-index property of the parent of the given | ||
219 | * node. This is used for DMA channel nodes that need to know the DMA ID | ||
220 | * of the controller they are on. | ||
221 | */ | ||
222 | static int get_parent_cell_index(struct device_node *np) | ||
223 | { | ||
224 | struct device_node *parent = of_get_parent(np); | ||
225 | const u32 *iprop; | ||
226 | |||
227 | if (!parent) | ||
228 | return -1; | ||
229 | |||
230 | iprop = of_get_property(parent, "cell-index", NULL); | ||
231 | of_node_put(parent); | ||
232 | |||
233 | if (!iprop) | ||
234 | return -1; | ||
235 | |||
236 | return *iprop; | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * codec_node_dev_name - determine the dev_name for a codec node | ||
194 | * | 241 | * |
195 | * FIXME: Currently, we only support one DMA controller, so if there are | 242 | * 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. | 243 | * that would be returned by dev_name() if this device_node were part of a |
244 | * 'struct device' It's ugly and hackish, but it works. | ||
197 | * | 245 | * |
198 | * FIXME: Even if we did support multiple DMA controllers, we have no | 246 | * 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 | 247 | * example, "cs4270-codec.0-004f". |
200 | * SSI devices. We also probably aren't compatible with the generic Elo DMA | ||
201 | * device driver. | ||
202 | */ | 248 | */ |
203 | static int mpc8610_hpcd_probe(struct platform_device *ofdev, | 249 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) |
204 | const struct of_device_id *match) | ||
205 | { | 250 | { |
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; | 251 | const u32 *iprop; |
252 | int bus, addr; | ||
253 | char temp[DAI_NAME_SIZE]; | ||
254 | |||
255 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
256 | |||
257 | iprop = of_get_property(np, "reg", NULL); | ||
258 | if (!iprop) | ||
259 | return -EINVAL; | ||
260 | |||
261 | addr = *iprop; | ||
262 | |||
263 | bus = get_parent_cell_index(np); | ||
264 | if (bus < 0) | ||
265 | return bus; | ||
266 | |||
267 | snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int get_dma_channel(struct device_node *ssi_np, | ||
273 | const char *compatible, | ||
274 | struct snd_soc_dai_link *dai, | ||
275 | unsigned int *dma_channel_id, | ||
276 | unsigned int *dma_id) | ||
277 | { | ||
214 | struct resource res; | 278 | struct resource res; |
279 | struct device_node *dma_channel_np; | ||
280 | const u32 *iprop; | ||
281 | int ret; | ||
282 | |||
283 | dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, | ||
284 | "fsl,ssi-dma-channel"); | ||
285 | if (!dma_channel_np) | ||
286 | return -EINVAL; | ||
287 | |||
288 | /* Determine the dev_name for the device_node. This code mimics the | ||
289 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
290 | * the dev_name() of the device to match the platform (DMA) device with | ||
291 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
292 | * now). | ||
293 | * | ||
294 | * dai->platform name should already point to an allocated buffer. | ||
295 | */ | ||
296 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
297 | if (ret) | ||
298 | return ret; | ||
299 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
300 | (unsigned long long) res.start, dma_channel_np->name); | ||
301 | |||
302 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
303 | if (!iprop) { | ||
304 | of_node_put(dma_channel_np); | ||
305 | return -EINVAL; | ||
306 | } | ||
307 | |||
308 | *dma_channel_id = *iprop; | ||
309 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
310 | of_node_put(dma_channel_np); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * mpc8610_hpcd_probe: platform probe function for the machine driver | ||
317 | * | ||
318 | * Although this is a machine driver, the SSI node is the "master" node with | ||
319 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
320 | * device for each new SSI node that has a codec attached. | ||
321 | */ | ||
322 | static int mpc8610_hpcd_probe(struct platform_device *pdev) | ||
323 | { | ||
324 | struct device *dev = pdev->dev.parent; | ||
325 | /* ssi_pdev is the platform device for the SSI node that probed us */ | ||
326 | struct platform_device *ssi_pdev = | ||
327 | container_of(dev, struct platform_device, dev); | ||
328 | struct device_node *np = ssi_pdev->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(&ssi_pdev->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 platform_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,154 @@ static int mpc8610_hpcd_probe(struct platform_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 | } |
499 | dev_set_drvdata(&pdev->dev, sound_device); | ||
489 | 500 | ||
490 | dev_set_drvdata(&ofdev->dev, sound_device); | 501 | of_node_put(codec_np); |
491 | 502 | ||
492 | return 0; | 503 | return 0; |
493 | 504 | ||
494 | error: | 505 | error: |
495 | of_node_put(codec_np); | 506 | 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 | 507 | ||
500 | if (sound_device) | 508 | if (sound_device) |
501 | platform_device_unregister(sound_device); | 509 | platform_device_unregister(sound_device); |
502 | 510 | ||
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); | 511 | kfree(machine_data); |
528 | 512 | ||
529 | return ret; | 513 | return ret; |
530 | } | 514 | } |
531 | 515 | ||
532 | /** | 516 | /** |
533 | * mpc8610_hpcd_remove: remove the OF device | 517 | * mpc8610_hpcd_remove: remove the platform device |
534 | * | 518 | * |
535 | * This function is called when the OF device is removed. | 519 | * This function is called when the platform device is removed. |
536 | */ | 520 | */ |
537 | static int mpc8610_hpcd_remove(struct platform_device *ofdev) | 521 | static int __devexit mpc8610_hpcd_remove(struct platform_device *pdev) |
538 | { | 522 | { |
539 | struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); | 523 | struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); |
524 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
540 | struct mpc8610_hpcd_data *machine_data = | 525 | struct mpc8610_hpcd_data *machine_data = |
541 | sound_device->dev.platform_data; | 526 | container_of(card, struct mpc8610_hpcd_data, card); |
542 | 527 | ||
543 | platform_device_unregister(sound_device); | 528 | platform_device_unregister(sound_device); |
544 | 529 | ||
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); | 530 | kfree(machine_data); |
567 | sound_device->dev.platform_data = NULL; | 531 | sound_device->dev.platform_data = NULL; |
568 | 532 | ||
569 | dev_set_drvdata(&ofdev->dev, NULL); | 533 | dev_set_drvdata(&pdev->dev, NULL); |
570 | 534 | ||
571 | return 0; | 535 | return 0; |
572 | } | 536 | } |
573 | 537 | ||
574 | static struct of_device_id mpc8610_hpcd_match[] = { | 538 | static struct platform_driver mpc8610_hpcd_driver = { |
575 | { | 539 | .probe = mpc8610_hpcd_probe, |
576 | .compatible = "fsl,mpc8610-ssi", | 540 | .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 = { | 541 | .driver = { |
584 | .name = "mpc8610_hpcd", | 542 | /* The name must match the 'model' property in the device tree, |
543 | * in lowercase letters. | ||
544 | */ | ||
545 | .name = "snd-soc-mpc8610hpcd", | ||
585 | .owner = THIS_MODULE, | 546 | .owner = THIS_MODULE, |
586 | .of_match_table = mpc8610_hpcd_match, | ||
587 | }, | 547 | }, |
588 | .probe = mpc8610_hpcd_probe, | ||
589 | .remove = mpc8610_hpcd_remove, | ||
590 | }; | 548 | }; |
591 | 549 | ||
592 | /** | 550 | /** |
593 | * mpc8610_hpcd_init: fabric driver initialization. | 551 | * mpc8610_hpcd_init: machine driver initialization. |
594 | * | 552 | * |
595 | * This function is called when this module is loaded. | 553 | * This function is called when this module is loaded. |
596 | */ | 554 | */ |
597 | static int __init mpc8610_hpcd_init(void) | 555 | static int __init mpc8610_hpcd_init(void) |
598 | { | 556 | { |
599 | int ret; | 557 | struct device_node *guts_np; |
600 | 558 | struct resource res; | |
601 | printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); | ||
602 | 559 | ||
603 | ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); | 560 | pr_info("Freescale MPC8610 HPCD ALSA SoC machine driver\n"); |
604 | 561 | ||
605 | if (ret) | 562 | /* Get the physical address of the global utilities registers */ |
606 | printk(KERN_ERR | 563 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); |
607 | "mpc8610-hpcd: failed to register platform driver\n"); | 564 | if (of_address_to_resource(guts_np, 0, &res)) { |
565 | pr_err("mpc8610-hpcd: missing/invalid global utilities node\n"); | ||
566 | return -EINVAL; | ||
567 | } | ||
568 | guts_phys = res.start; | ||
608 | 569 | ||
609 | return ret; | 570 | return platform_driver_register(&mpc8610_hpcd_driver); |
610 | } | 571 | } |
611 | 572 | ||
612 | /** | 573 | /** |
613 | * mpc8610_hpcd_exit: fabric driver exit | 574 | * mpc8610_hpcd_exit: machine driver exit |
614 | * | 575 | * |
615 | * This function is called when this driver is unloaded. | 576 | * This function is called when this driver is unloaded. |
616 | */ | 577 | */ |
617 | static void __exit mpc8610_hpcd_exit(void) | 578 | static void __exit mpc8610_hpcd_exit(void) |
618 | { | 579 | { |
619 | of_unregister_platform_driver(&mpc8610_hpcd_of_driver); | 580 | platform_driver_unregister(&mpc8610_hpcd_driver); |
620 | } | 581 | } |
621 | 582 | ||
622 | module_init(mpc8610_hpcd_init); | 583 | module_init(mpc8610_hpcd_init); |
623 | module_exit(mpc8610_hpcd_exit); | 584 | module_exit(mpc8610_hpcd_exit); |
624 | 585 | ||
625 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 586 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
626 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); | 587 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC machine driver"); |
627 | MODULE_LICENSE("GPL"); | 588 | MODULE_LICENSE("GPL v2"); |