diff options
Diffstat (limited to 'sound/aoa/soundbus/i2sbus/core.c')
-rw-r--r-- | sound/aoa/soundbus/i2sbus/core.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c new file mode 100644 index 00000000000..d1b5cff9249 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/core.c | |||
@@ -0,0 +1,450 @@ | |||
1 | /* | ||
2 | * i2sbus driver | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/dma-mapping.h> | ||
13 | |||
14 | #include <sound/core.h> | ||
15 | |||
16 | #include <asm/macio.h> | ||
17 | #include <asm/dbdma.h> | ||
18 | |||
19 | #include "../soundbus.h" | ||
20 | #include "i2sbus.h" | ||
21 | |||
22 | MODULE_LICENSE("GPL"); | ||
23 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
24 | MODULE_DESCRIPTION("Apple Soundbus: I2S support"); | ||
25 | |||
26 | static int force; | ||
27 | module_param(force, int, 0444); | ||
28 | MODULE_PARM_DESC(force, "Force loading i2sbus even when" | ||
29 | " no layout-id property is present"); | ||
30 | |||
31 | static struct of_device_id i2sbus_match[] = { | ||
32 | { .name = "i2s" }, | ||
33 | { } | ||
34 | }; | ||
35 | |||
36 | MODULE_DEVICE_TABLE(of, i2sbus_match); | ||
37 | |||
38 | static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, | ||
39 | struct dbdma_command_mem *r, | ||
40 | int numcmds) | ||
41 | { | ||
42 | /* one more for rounding, one for branch back, one for stop command */ | ||
43 | r->size = (numcmds + 3) * sizeof(struct dbdma_cmd); | ||
44 | /* We use the PCI APIs for now until the generic one gets fixed | ||
45 | * enough or until we get some macio-specific versions | ||
46 | */ | ||
47 | r->space = dma_alloc_coherent( | ||
48 | &macio_get_pci_dev(i2sdev->macio)->dev, | ||
49 | r->size, | ||
50 | &r->bus_addr, | ||
51 | GFP_KERNEL); | ||
52 | |||
53 | if (!r->space) return -ENOMEM; | ||
54 | |||
55 | memset(r->space, 0, r->size); | ||
56 | r->cmds = (void*)DBDMA_ALIGN(r->space); | ||
57 | r->bus_cmd_start = r->bus_addr + | ||
58 | (dma_addr_t)((char*)r->cmds - (char*)r->space); | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, | ||
64 | struct dbdma_command_mem *r) | ||
65 | { | ||
66 | if (!r->space) return; | ||
67 | |||
68 | dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev, | ||
69 | r->size, r->space, r->bus_addr); | ||
70 | } | ||
71 | |||
72 | static void i2sbus_release_dev(struct device *dev) | ||
73 | { | ||
74 | struct i2sbus_dev *i2sdev; | ||
75 | int i; | ||
76 | |||
77 | i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev); | ||
78 | |||
79 | if (i2sdev->intfregs) iounmap(i2sdev->intfregs); | ||
80 | if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); | ||
81 | if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); | ||
82 | for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) | ||
83 | if (i2sdev->allocated_resource[i]) | ||
84 | release_and_free_resource(i2sdev->allocated_resource[i]); | ||
85 | free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); | ||
86 | free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring); | ||
87 | for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) | ||
88 | free_irq(i2sdev->interrupts[i], i2sdev); | ||
89 | i2sbus_control_remove_dev(i2sdev->control, i2sdev); | ||
90 | mutex_destroy(&i2sdev->lock); | ||
91 | kfree(i2sdev); | ||
92 | } | ||
93 | |||
94 | static irqreturn_t i2sbus_bus_intr(int irq, void *devid) | ||
95 | { | ||
96 | struct i2sbus_dev *dev = devid; | ||
97 | u32 intreg; | ||
98 | |||
99 | spin_lock(&dev->low_lock); | ||
100 | intreg = in_le32(&dev->intfregs->intr_ctl); | ||
101 | |||
102 | /* acknowledge interrupt reasons */ | ||
103 | out_le32(&dev->intfregs->intr_ctl, intreg); | ||
104 | |||
105 | spin_unlock(&dev->low_lock); | ||
106 | |||
107 | return IRQ_HANDLED; | ||
108 | } | ||
109 | |||
110 | |||
111 | /* | ||
112 | * XXX FIXME: We test the layout_id's here to get the proper way of | ||
113 | * mapping in various registers, thanks to bugs in Apple device-trees. | ||
114 | * We could instead key off the machine model and the name of the i2s | ||
115 | * node (i2s-a). This we'll do when we move it all to macio_asic.c | ||
116 | * and have that export items for each sub-node too. | ||
117 | */ | ||
118 | static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index, | ||
119 | int layout, struct resource *res) | ||
120 | { | ||
121 | struct device_node *parent; | ||
122 | int pindex, rc = -ENXIO; | ||
123 | const u32 *reg; | ||
124 | |||
125 | /* Machines with layout 76 and 36 (K2 based) have a weird device | ||
126 | * tree what we need to special case. | ||
127 | * Normal machines just fetch the resource from the i2s-X node. | ||
128 | * Darwin further divides normal machines into old and new layouts | ||
129 | * with a subtely different code path but that doesn't seem necessary | ||
130 | * in practice, they just bloated it. In addition, even on our K2 | ||
131 | * case the i2s-modem node, if we ever want to handle it, uses the | ||
132 | * normal layout | ||
133 | */ | ||
134 | if (layout != 76 && layout != 36) | ||
135 | return of_address_to_resource(np, index, res); | ||
136 | |||
137 | parent = of_get_parent(np); | ||
138 | pindex = (index == aoa_resource_i2smmio) ? 0 : 1; | ||
139 | rc = of_address_to_resource(parent, pindex, res); | ||
140 | if (rc) | ||
141 | goto bail; | ||
142 | reg = of_get_property(np, "reg", NULL); | ||
143 | if (reg == NULL) { | ||
144 | rc = -ENXIO; | ||
145 | goto bail; | ||
146 | } | ||
147 | res->start += reg[index * 2]; | ||
148 | res->end = res->start + reg[index * 2 + 1] - 1; | ||
149 | bail: | ||
150 | of_node_put(parent); | ||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | /* FIXME: look at device node refcounting */ | ||
155 | static int i2sbus_add_dev(struct macio_dev *macio, | ||
156 | struct i2sbus_control *control, | ||
157 | struct device_node *np) | ||
158 | { | ||
159 | struct i2sbus_dev *dev; | ||
160 | struct device_node *child = NULL, *sound = NULL; | ||
161 | struct resource *r; | ||
162 | int i, layout = 0, rlen; | ||
163 | static const char *rnames[] = { "i2sbus: %s (control)", | ||
164 | "i2sbus: %s (tx)", | ||
165 | "i2sbus: %s (rx)" }; | ||
166 | static irq_handler_t ints[] = { | ||
167 | i2sbus_bus_intr, | ||
168 | i2sbus_tx_intr, | ||
169 | i2sbus_rx_intr | ||
170 | }; | ||
171 | |||
172 | if (strlen(np->name) != 5) | ||
173 | return 0; | ||
174 | if (strncmp(np->name, "i2s-", 4)) | ||
175 | return 0; | ||
176 | |||
177 | dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL); | ||
178 | if (!dev) | ||
179 | return 0; | ||
180 | |||
181 | i = 0; | ||
182 | while ((child = of_get_next_child(np, child))) { | ||
183 | if (strcmp(child->name, "sound") == 0) { | ||
184 | i++; | ||
185 | sound = child; | ||
186 | } | ||
187 | } | ||
188 | if (i == 1) { | ||
189 | const u32 *layout_id = | ||
190 | of_get_property(sound, "layout-id", NULL); | ||
191 | if (layout_id) { | ||
192 | layout = *layout_id; | ||
193 | snprintf(dev->sound.modalias, 32, | ||
194 | "sound-layout-%d", layout); | ||
195 | force = 1; | ||
196 | } | ||
197 | } | ||
198 | /* for the time being, until we can handle non-layout-id | ||
199 | * things in some fabric, refuse to attach if there is no | ||
200 | * layout-id property or we haven't been forced to attach. | ||
201 | * When there are two i2s busses and only one has a layout-id, | ||
202 | * then this depends on the order, but that isn't important | ||
203 | * either as the second one in that case is just a modem. */ | ||
204 | if (!force) { | ||
205 | kfree(dev); | ||
206 | return -ENODEV; | ||
207 | } | ||
208 | |||
209 | mutex_init(&dev->lock); | ||
210 | spin_lock_init(&dev->low_lock); | ||
211 | dev->sound.ofdev.node = np; | ||
212 | dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask; | ||
213 | dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask; | ||
214 | dev->sound.ofdev.dev.parent = &macio->ofdev.dev; | ||
215 | dev->sound.ofdev.dev.release = i2sbus_release_dev; | ||
216 | dev->sound.attach_codec = i2sbus_attach_codec; | ||
217 | dev->sound.detach_codec = i2sbus_detach_codec; | ||
218 | dev->sound.pcmid = -1; | ||
219 | dev->macio = macio; | ||
220 | dev->control = control; | ||
221 | dev->bus_number = np->name[4] - 'a'; | ||
222 | INIT_LIST_HEAD(&dev->sound.codec_list); | ||
223 | |||
224 | for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { | ||
225 | dev->interrupts[i] = -1; | ||
226 | snprintf(dev->rnames[i], sizeof(dev->rnames[i]), | ||
227 | rnames[i], np->name); | ||
228 | } | ||
229 | for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { | ||
230 | int irq = irq_of_parse_and_map(np, i); | ||
231 | if (request_irq(irq, ints[i], 0, dev->rnames[i], dev)) | ||
232 | goto err; | ||
233 | dev->interrupts[i] = irq; | ||
234 | } | ||
235 | |||
236 | |||
237 | /* Resource handling is problematic as some device-trees contain | ||
238 | * useless crap (ugh ugh ugh). We work around that here by calling | ||
239 | * specific functions for calculating the appropriate resources. | ||
240 | * | ||
241 | * This will all be moved to macio_asic.c at one point | ||
242 | */ | ||
243 | for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { | ||
244 | if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i])) | ||
245 | goto err; | ||
246 | /* If only we could use our resource dev->resources[i]... | ||
247 | * but request_resource doesn't know about parents and | ||
248 | * contained resources... | ||
249 | */ | ||
250 | dev->allocated_resource[i] = | ||
251 | request_mem_region(dev->resources[i].start, | ||
252 | dev->resources[i].end - | ||
253 | dev->resources[i].start + 1, | ||
254 | dev->rnames[i]); | ||
255 | if (!dev->allocated_resource[i]) { | ||
256 | printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i); | ||
257 | goto err; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | r = &dev->resources[aoa_resource_i2smmio]; | ||
262 | rlen = r->end - r->start + 1; | ||
263 | if (rlen < sizeof(struct i2s_interface_regs)) | ||
264 | goto err; | ||
265 | dev->intfregs = ioremap(r->start, rlen); | ||
266 | |||
267 | r = &dev->resources[aoa_resource_txdbdma]; | ||
268 | rlen = r->end - r->start + 1; | ||
269 | if (rlen < sizeof(struct dbdma_regs)) | ||
270 | goto err; | ||
271 | dev->out.dbdma = ioremap(r->start, rlen); | ||
272 | |||
273 | r = &dev->resources[aoa_resource_rxdbdma]; | ||
274 | rlen = r->end - r->start + 1; | ||
275 | if (rlen < sizeof(struct dbdma_regs)) | ||
276 | goto err; | ||
277 | dev->in.dbdma = ioremap(r->start, rlen); | ||
278 | |||
279 | if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma) | ||
280 | goto err; | ||
281 | |||
282 | if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring, | ||
283 | MAX_DBDMA_COMMANDS)) | ||
284 | goto err; | ||
285 | if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring, | ||
286 | MAX_DBDMA_COMMANDS)) | ||
287 | goto err; | ||
288 | |||
289 | if (i2sbus_control_add_dev(dev->control, dev)) { | ||
290 | printk(KERN_ERR "i2sbus: control layer didn't like bus\n"); | ||
291 | goto err; | ||
292 | } | ||
293 | |||
294 | if (soundbus_add_one(&dev->sound)) { | ||
295 | printk(KERN_DEBUG "i2sbus: device registration error!\n"); | ||
296 | goto err; | ||
297 | } | ||
298 | |||
299 | /* enable this cell */ | ||
300 | i2sbus_control_cell(dev->control, dev, 1); | ||
301 | i2sbus_control_enable(dev->control, dev); | ||
302 | i2sbus_control_clock(dev->control, dev, 1); | ||
303 | |||
304 | return 1; | ||
305 | err: | ||
306 | for (i=0;i<3;i++) | ||
307 | if (dev->interrupts[i] != -1) | ||
308 | free_irq(dev->interrupts[i], dev); | ||
309 | free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring); | ||
310 | free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring); | ||
311 | if (dev->intfregs) iounmap(dev->intfregs); | ||
312 | if (dev->out.dbdma) iounmap(dev->out.dbdma); | ||
313 | if (dev->in.dbdma) iounmap(dev->in.dbdma); | ||
314 | for (i=0;i<3;i++) | ||
315 | if (dev->allocated_resource[i]) | ||
316 | release_and_free_resource(dev->allocated_resource[i]); | ||
317 | mutex_destroy(&dev->lock); | ||
318 | kfree(dev); | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match) | ||
323 | { | ||
324 | struct device_node *np = NULL; | ||
325 | int got = 0, err; | ||
326 | struct i2sbus_control *control = NULL; | ||
327 | |||
328 | err = i2sbus_control_init(dev, &control); | ||
329 | if (err) | ||
330 | return err; | ||
331 | if (!control) { | ||
332 | printk(KERN_ERR "i2sbus_control_init API breakage\n"); | ||
333 | return -ENODEV; | ||
334 | } | ||
335 | |||
336 | while ((np = of_get_next_child(dev->ofdev.node, np))) { | ||
337 | if (of_device_is_compatible(np, "i2sbus") || | ||
338 | of_device_is_compatible(np, "i2s-modem")) { | ||
339 | got += i2sbus_add_dev(dev, control, np); | ||
340 | } | ||
341 | } | ||
342 | |||
343 | if (!got) { | ||
344 | /* found none, clean up */ | ||
345 | i2sbus_control_destroy(control); | ||
346 | return -ENODEV; | ||
347 | } | ||
348 | |||
349 | dev->ofdev.dev.driver_data = control; | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int i2sbus_remove(struct macio_dev* dev) | ||
355 | { | ||
356 | struct i2sbus_control *control = dev->ofdev.dev.driver_data; | ||
357 | struct i2sbus_dev *i2sdev, *tmp; | ||
358 | |||
359 | list_for_each_entry_safe(i2sdev, tmp, &control->list, item) | ||
360 | soundbus_remove_one(&i2sdev->sound); | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | #ifdef CONFIG_PM | ||
366 | static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) | ||
367 | { | ||
368 | struct i2sbus_control *control = dev->ofdev.dev.driver_data; | ||
369 | struct codec_info_item *cii; | ||
370 | struct i2sbus_dev* i2sdev; | ||
371 | int err, ret = 0; | ||
372 | |||
373 | list_for_each_entry(i2sdev, &control->list, item) { | ||
374 | /* Notify Alsa */ | ||
375 | if (i2sdev->sound.pcm) { | ||
376 | /* Suspend PCM streams */ | ||
377 | snd_pcm_suspend_all(i2sdev->sound.pcm); | ||
378 | } | ||
379 | |||
380 | /* Notify codecs */ | ||
381 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
382 | err = 0; | ||
383 | if (cii->codec->suspend) | ||
384 | err = cii->codec->suspend(cii, state); | ||
385 | if (err) | ||
386 | ret = err; | ||
387 | } | ||
388 | |||
389 | /* wait until streams are stopped */ | ||
390 | i2sbus_wait_for_stop_both(i2sdev); | ||
391 | } | ||
392 | |||
393 | return ret; | ||
394 | } | ||
395 | |||
396 | static int i2sbus_resume(struct macio_dev* dev) | ||
397 | { | ||
398 | struct i2sbus_control *control = dev->ofdev.dev.driver_data; | ||
399 | struct codec_info_item *cii; | ||
400 | struct i2sbus_dev* i2sdev; | ||
401 | int err, ret = 0; | ||
402 | |||
403 | list_for_each_entry(i2sdev, &control->list, item) { | ||
404 | /* reset i2s bus format etc. */ | ||
405 | i2sbus_pcm_prepare_both(i2sdev); | ||
406 | |||
407 | /* Notify codecs so they can re-initialize */ | ||
408 | list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { | ||
409 | err = 0; | ||
410 | if (cii->codec->resume) | ||
411 | err = cii->codec->resume(cii); | ||
412 | if (err) | ||
413 | ret = err; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | return ret; | ||
418 | } | ||
419 | #endif /* CONFIG_PM */ | ||
420 | |||
421 | static int i2sbus_shutdown(struct macio_dev* dev) | ||
422 | { | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static struct macio_driver i2sbus_drv = { | ||
427 | .name = "soundbus-i2s", | ||
428 | .owner = THIS_MODULE, | ||
429 | .match_table = i2sbus_match, | ||
430 | .probe = i2sbus_probe, | ||
431 | .remove = i2sbus_remove, | ||
432 | #ifdef CONFIG_PM | ||
433 | .suspend = i2sbus_suspend, | ||
434 | .resume = i2sbus_resume, | ||
435 | #endif | ||
436 | .shutdown = i2sbus_shutdown, | ||
437 | }; | ||
438 | |||
439 | static int __init soundbus_i2sbus_init(void) | ||
440 | { | ||
441 | return macio_register_driver(&i2sbus_drv); | ||
442 | } | ||
443 | |||
444 | static void __exit soundbus_i2sbus_exit(void) | ||
445 | { | ||
446 | macio_unregister_driver(&i2sbus_drv); | ||
447 | } | ||
448 | |||
449 | module_init(soundbus_i2sbus_init); | ||
450 | module_exit(soundbus_i2sbus_exit); | ||