diff options
Diffstat (limited to 'sound/aoa/soundbus/core.c')
-rw-r--r-- | sound/aoa/soundbus/core.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c new file mode 100644 index 000000000000..abe84a76c835 --- /dev/null +++ b/sound/aoa/soundbus/core.c @@ -0,0 +1,250 @@ +/* + * soundbus + * + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + * + * GPL v2, can be found in COPYING. + */ + +#include <linux/module.h> +#include "soundbus.h" + +MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Apple Soundbus"); + +struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev) +{ + struct device *tmp; + + if (!dev) + return NULL; + tmp = get_device(&dev->ofdev.dev); + if (tmp) + return to_soundbus_device(tmp); + else + return NULL; +} +EXPORT_SYMBOL_GPL(soundbus_dev_get); + +void soundbus_dev_put(struct soundbus_dev *dev) +{ + if (dev) + put_device(&dev->ofdev.dev); +} +EXPORT_SYMBOL_GPL(soundbus_dev_put); + +static int soundbus_probe(struct device *dev) +{ + int error = -ENODEV; + struct soundbus_driver *drv; + struct soundbus_dev *soundbus_dev; + + drv = to_soundbus_driver(dev->driver); + soundbus_dev = to_soundbus_device(dev); + + if (!drv->probe) + return error; + + soundbus_dev_get(soundbus_dev); + + error = drv->probe(soundbus_dev); + if (error) + soundbus_dev_put(soundbus_dev); + + return error; +} + + +static int soundbus_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct soundbus_dev * soundbus_dev; + struct of_device * of; + char *scratch, *compat, *compat2; + int i = 0; + int length, cplen, cplen2, seen = 0; + + if (!dev) + return -ENODEV; + + soundbus_dev = to_soundbus_device(dev); + if (!soundbus_dev) + return -ENODEV; + + of = &soundbus_dev->ofdev; + + /* stuff we want to pass to /sbin/hotplug */ + envp[i++] = scratch = buffer; + length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + + /* Since the compatible field can contain pretty much anything + * it's not really legal to split it out with commas. We split it + * up using a number of environment variables instead. */ + + compat = (char *) get_property(of->node, "compatible", &cplen); + compat2 = compat; + cplen2= cplen; + while (compat && cplen > 0) { + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, + "OF_COMPATIBLE_%d=%s", seen, compat); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + length = strlen (compat) + 1; + compat += length; + cplen -= length; + seen++; + } + + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, "MODALIAS=%s", + soundbus_dev->modalias); + + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +static int soundbus_device_remove(struct device *dev) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->remove) + drv->remove(soundbus_dev); + soundbus_dev_put(soundbus_dev); + + return 0; +} + +static void soundbus_device_shutdown(struct device *dev) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(soundbus_dev); +} + +#ifdef CONFIG_PM + +static int soundbus_device_suspend(struct device *dev, pm_message_t state) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->suspend) + return drv->suspend(soundbus_dev, state); + return 0; +} + +static int soundbus_device_resume(struct device * dev) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->resume) + return drv->resume(soundbus_dev); + return 0; +} + +#endif /* CONFIG_PM */ + +extern struct device_attribute soundbus_dev_attrs[]; + +static struct bus_type soundbus_bus_type = { + .name = "aoa-soundbus", + .probe = soundbus_probe, + .uevent = soundbus_uevent, + .remove = soundbus_device_remove, + .shutdown = soundbus_device_shutdown, +#ifdef CONFIG_PM + .suspend = soundbus_device_suspend, + .resume = soundbus_device_resume, +#endif + .dev_attrs = soundbus_dev_attrs, +}; + +static int __init soundbus_init(void) +{ + return bus_register(&soundbus_bus_type); +} + +static void __exit soundbus_exit(void) +{ + bus_unregister(&soundbus_bus_type); +} + +int soundbus_add_one(struct soundbus_dev *dev) +{ + static int devcount; + + /* sanity checks */ + if (!dev->attach_codec || + !dev->ofdev.node || + dev->pcmname || + dev->pcmid != -1) { + printk(KERN_ERR "soundbus: adding device failed sanity check!\n"); + return -EINVAL; + } + + snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount); + dev->ofdev.dev.bus = &soundbus_bus_type; + return of_device_register(&dev->ofdev); +} +EXPORT_SYMBOL_GPL(soundbus_add_one); + +void soundbus_remove_one(struct soundbus_dev *dev) +{ + of_device_unregister(&dev->ofdev); +} +EXPORT_SYMBOL_GPL(soundbus_remove_one); + +int soundbus_register_driver(struct soundbus_driver *drv) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &soundbus_bus_type; + + /* register with core */ + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(soundbus_register_driver); + +void soundbus_unregister_driver(struct soundbus_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(soundbus_unregister_driver); + +module_init(soundbus_init); +module_exit(soundbus_exit); |