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