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