| 
									
										
										
										
											2006-06-21 15:42:43 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Apple Onboard Audio driver core | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * GPL v2, can be found in COPYING. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/list.h>
 | 
					
						
							|  |  |  | #include "../aoa.h"
 | 
					
						
							| 
									
										
										
										
											2008-10-23 15:47:56 +02:00
										 |  |  | #include "alsa.h"
 | 
					
						
							| 
									
										
										
										
											2006-06-21 15:42:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver"); | 
					
						
							|  |  |  | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* We allow only one fabric. This simplifies things,
 | 
					
						
							|  |  |  |  * and more don't really make that much sense */ | 
					
						
							|  |  |  | static struct aoa_fabric *fabric; | 
					
						
							|  |  |  | static LIST_HEAD(codec_list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int attach_codec_to_fabric(struct aoa_codec *c) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!try_module_get(c->owner)) | 
					
						
							|  |  |  | 		return -EBUSY; | 
					
						
							|  |  |  | 	/* found_codec has to be assigned */ | 
					
						
							|  |  |  | 	err = -ENOENT; | 
					
						
							|  |  |  | 	if (fabric->found_codec) | 
					
						
							|  |  |  | 		err = fabric->found_codec(c); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		module_put(c->owner); | 
					
						
							|  |  |  | 		printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n", | 
					
						
							|  |  |  | 				c->name); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	c->fabric = fabric; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = 0; | 
					
						
							|  |  |  | 	if (c->init) | 
					
						
							|  |  |  | 		err = c->init(c); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name); | 
					
						
							|  |  |  | 		c->fabric = NULL; | 
					
						
							|  |  |  | 		if (fabric->remove_codec) | 
					
						
							|  |  |  | 			fabric->remove_codec(c); | 
					
						
							|  |  |  | 		module_put(c->owner); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (fabric->attached_codec) | 
					
						
							|  |  |  | 		fabric->attached_codec(c); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int aoa_codec_register(struct aoa_codec *codec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if there's a fabric already, we can tell if we
 | 
					
						
							|  |  |  | 	 * will want to have this codec, so propagate error | 
					
						
							|  |  |  | 	 * through. Otherwise, this will happen later... */ | 
					
						
							|  |  |  | 	if (fabric) | 
					
						
							|  |  |  | 		err = attach_codec_to_fabric(codec); | 
					
						
							|  |  |  | 	if (!err) | 
					
						
							|  |  |  | 		list_add(&codec->list, &codec_list); | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(aoa_codec_register); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void aoa_codec_unregister(struct aoa_codec *codec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	list_del(&codec->list); | 
					
						
							|  |  |  | 	if (codec->fabric && codec->exit) | 
					
						
							|  |  |  | 		codec->exit(codec); | 
					
						
							|  |  |  | 	if (fabric && fabric->remove_codec) | 
					
						
							|  |  |  | 		fabric->remove_codec(codec); | 
					
						
							|  |  |  | 	codec->fabric = NULL; | 
					
						
							|  |  |  | 	module_put(codec->owner); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(aoa_codec_unregister); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-07 08:24:12 +01:00
										 |  |  | int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev) | 
					
						
							| 
									
										
										
										
											2006-06-21 15:42:43 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct aoa_codec *c; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* allow querying for presence of fabric
 | 
					
						
							|  |  |  | 	 * (i.e. do this test first!) */ | 
					
						
							|  |  |  | 	if (new_fabric == fabric) { | 
					
						
							|  |  |  | 		err = -EALREADY; | 
					
						
							|  |  |  | 		goto attach; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (fabric) | 
					
						
							|  |  |  | 		return -EEXIST; | 
					
						
							|  |  |  | 	if (!new_fabric) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-12-07 08:24:12 +01:00
										 |  |  | 	err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev); | 
					
						
							| 
									
										
										
										
											2006-06-21 15:42:43 +02:00
										 |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fabric = new_fabric; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  attach: | 
					
						
							|  |  |  | 	list_for_each_entry(c, &codec_list, list) { | 
					
						
							|  |  |  | 		if (c->fabric != fabric) | 
					
						
							|  |  |  | 			attach_codec_to_fabric(c); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(aoa_fabric_register); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void aoa_fabric_unregister(struct aoa_fabric *old_fabric) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct aoa_codec *c; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fabric != old_fabric) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(c, &codec_list, list) { | 
					
						
							|  |  |  | 		if (c->fabric) | 
					
						
							|  |  |  | 			aoa_fabric_unlink_codec(c); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	aoa_alsa_cleanup(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fabric = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(aoa_fabric_unregister); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void aoa_fabric_unlink_codec(struct aoa_codec *codec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!codec->fabric) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "snd-aoa: fabric unassigned " | 
					
						
							|  |  |  | 				"in aoa_fabric_unlink_codec\n"); | 
					
						
							|  |  |  | 		dump_stack(); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (codec->exit) | 
					
						
							|  |  |  | 		codec->exit(codec); | 
					
						
							|  |  |  | 	if (codec->fabric->remove_codec) | 
					
						
							|  |  |  | 		codec->fabric->remove_codec(codec); | 
					
						
							|  |  |  | 	codec->fabric = NULL; | 
					
						
							|  |  |  | 	module_put(codec->owner); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init aoa_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit aoa_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	aoa_alsa_cleanup(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(aoa_init); | 
					
						
							|  |  |  | module_exit(aoa_exit); |