From 72496edcf85e048b4c5373d518e4f27938d9594e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Feb 2015 22:39:51 +0100 Subject: ALSA: seq: Don't compile snd_seq_device_load_drivers() for built-in Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index 2b5f24cc7548..d52433563da2 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -67,7 +67,11 @@ struct snd_seq_dev_ops { /* * prototypes */ +#ifdef CONFIG_MODULES void snd_seq_device_load_drivers(void); +#else +#define snd_seq_device_load_drivers() +#endif int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result); int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize); int snd_seq_device_unregister_driver(char *id); -- cgit v1.2.3 From 7c37ae5c625aaa4836466cfaea829a3199dfc571 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 10:51:59 +0100 Subject: ALSA: seq: Rewrite sequencer device binding with standard bus We've used the old house-made code for binding the sequencer device and driver. This can be far better implemented with the standard bus nowadays. This patch refactors the whole sequencer binding code with the bus /sys/bus/snd_seq. The devices appear as id-card-device on this bus and are bound with the drivers corresponding to the given id like the former implementation. The module autoload is also kept like before. There is no change in API functions by this patch, and almost all transitions are kept inside seq_device.c. The proc file output will change slightly but kept compatible as much as possible. Further integration works will follow in later patches. Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 3 + sound/core/seq/seq_device.c | 541 ++++++++++++++------------------------------ 2 files changed, 170 insertions(+), 374 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index d52433563da2..ea256b825146 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -43,8 +43,11 @@ struct snd_seq_device { void *private_data; /* private data for the caller */ void (*private_free)(struct snd_seq_device *device); struct list_head list; /* link to next device */ + struct device dev; }; +#define to_seq_dev(_dev) \ + container_of(_dev, struct snd_seq_device, dev) /* driver operators * init_device: diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 075a66c0cc6a..d3320ffe43c2 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -36,6 +36,7 @@ * */ +#include #include #include #include @@ -51,77 +52,57 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -/* driver state */ -#define DRIVER_EMPTY 0 -#define DRIVER_LOADED (1<<0) -#define DRIVER_REQUESTED (1<<1) -#define DRIVER_LOCKED (1<<2) -#define DRIVER_REQUESTING (1<<3) +struct snd_seq_driver { + struct device_driver driver; + char id[ID_LEN]; + int argsize; + struct snd_seq_dev_ops ops; +}; -struct ops_list { - char id[ID_LEN]; /* driver id */ - int driver; /* driver state */ - int used; /* reference counter */ - int argsize; /* argument size */ +#define to_seq_drv(_drv) \ + container_of(_drv, struct snd_seq_driver, driver) - /* operators */ - struct snd_seq_dev_ops ops; +/* + * bus definition + */ +static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_seq_driver *sdrv = to_seq_drv(drv); - /* registered devices */ - struct list_head dev_list; /* list of devices */ - int num_devices; /* number of associated devices */ - int num_init_devices; /* number of initialized devices */ - struct mutex reg_mutex; + return strcmp(sdrv->id, sdev->id) == 0 && + sdrv->argsize == sdev->argsize; +} - struct list_head list; /* next driver */ +static struct bus_type snd_seq_bus_type = { + .name = "snd_seq", + .match = snd_seq_bus_match, }; - -static LIST_HEAD(opslist); -static int num_ops; -static DEFINE_MUTEX(ops_mutex); +/* + * proc interface -- just for compatibility + */ #ifdef CONFIG_PROC_FS static struct snd_info_entry *info_entry; -#endif -/* - * prototypes - */ -static int snd_seq_device_free(struct snd_seq_device *dev); -static int snd_seq_device_dev_free(struct snd_device *device); -static int snd_seq_device_dev_register(struct snd_device *device); -static int snd_seq_device_dev_disconnect(struct snd_device *device); - -static int init_device(struct snd_seq_device *dev, struct ops_list *ops); -static int free_device(struct snd_seq_device *dev, struct ops_list *ops); -static struct ops_list *find_driver(char *id, int create_if_empty); -static struct ops_list *create_driver(char *id); -static void unlock_driver(struct ops_list *ops); -static void remove_drivers(void); +static int print_dev_info(struct device *dev, void *data) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_info_buffer *buffer = data; -/* - * show all drivers and their status - */ + snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id, + dev->driver ? "loaded" : "empty", + dev->driver ? 1 : 0); + return 0; +} -#ifdef CONFIG_PROC_FS static void snd_seq_device_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", - ops->id, - ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), - ops->driver & DRIVER_REQUESTED ? ",requested" : "", - ops->driver & DRIVER_LOCKED ? ",locked" : "", - ops->num_devices); - } - mutex_unlock(&ops_mutex); + bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info); } #endif - + /* * load all registered drivers (called from seq_clientmgr.c) */ @@ -141,52 +122,29 @@ void snd_seq_autoload_unlock(void) } EXPORT_SYMBOL(snd_seq_autoload_unlock); -static void autoload_drivers(void) +static int request_seq_drv(struct device *dev, void *data) { - /* avoid reentrance */ - if (atomic_inc_return(&snd_seq_in_init) == 1) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if ((ops->driver & DRIVER_REQUESTING) && - !(ops->driver & DRIVER_REQUESTED)) { - ops->used++; - mutex_unlock(&ops_mutex); - ops->driver |= DRIVER_REQUESTED; - request_module("snd-%s", ops->id); - mutex_lock(&ops_mutex); - ops->used--; - } - } - mutex_unlock(&ops_mutex); - } - atomic_dec(&snd_seq_in_init); -} + struct snd_seq_device *sdev = to_seq_dev(dev); -static void call_autoload(struct work_struct *work) -{ - autoload_drivers(); + if (!dev->driver) + request_module("snd-%s", sdev->id); + return 0; } -static DECLARE_WORK(autoload_work, call_autoload); - -static void try_autoload(struct ops_list *ops) +static void autoload_drivers(struct work_struct *work) { - if (!ops->driver) { - ops->driver |= DRIVER_REQUESTING; - schedule_work(&autoload_work); - } + /* avoid reentrance */ + if (atomic_inc_return(&snd_seq_in_init) == 1) + bus_for_each_dev(&snd_seq_bus_type, NULL, NULL, + request_seq_drv); + atomic_dec(&snd_seq_in_init); } +static DECLARE_WORK(autoload_work, autoload_drivers); + static void queue_autoload_drivers(void) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) - try_autoload(ops); - mutex_unlock(&ops_mutex); + schedule_work(&autoload_work); } void snd_seq_autoload_init(void) @@ -206,9 +164,50 @@ void snd_seq_device_load_drivers(void) } EXPORT_SYMBOL(snd_seq_device_load_drivers); #else -#define try_autoload(ops) /* NOP */ +#define queue_autoload_drivers() /* NOP */ #endif +/* + * device management + */ +static int snd_seq_device_dev_free(struct snd_device *device) +{ + struct snd_seq_device *dev = device->device_data; + + put_device(&dev->dev); + return 0; +} + +static int snd_seq_device_dev_register(struct snd_device *device) +{ + struct snd_seq_device *dev = device->device_data; + int err; + + err = device_add(&dev->dev); + if (err < 0) + return err; + if (!dev->dev.driver) + queue_autoload_drivers(); + return 0; +} + +static int snd_seq_device_dev_disconnect(struct snd_device *device) +{ + struct snd_seq_device *dev = device->device_data; + + device_del(&dev->dev); + return 0; +} + +static void snd_seq_dev_release(struct device *dev) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + + if (sdev->private_free) + sdev->private_free(sdev); + kfree(sdev); +} + /* * register a sequencer device * card = card info @@ -220,7 +219,6 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result) { struct snd_seq_device *dev; - struct ops_list *ops; int err; static struct snd_device_ops dops = { .dev_free = snd_seq_device_dev_free, @@ -234,15 +232,9 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, if (snd_BUG_ON(!id)) return -EINVAL; - ops = find_driver(id, 1); - if (ops == NULL) - return -ENOMEM; - - dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL); - if (dev == NULL) { - unlock_driver(ops); + dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL); + if (!dev) return -ENOMEM; - } /* set up device info */ dev->card = card; @@ -251,20 +243,19 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, dev->argsize = argsize; dev->status = SNDRV_SEQ_DEVICE_FREE; - /* add this device to the list */ - mutex_lock(&ops->reg_mutex); - list_add_tail(&dev->list, &ops->dev_list); - ops->num_devices++; - mutex_unlock(&ops->reg_mutex); + device_initialize(&dev->dev); + dev->dev.parent = &card->card_dev; + dev->dev.bus = &snd_seq_bus_type; + dev->dev.release = snd_seq_dev_release; + dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device); - if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { - snd_seq_device_free(dev); + /* add this device to the list */ + err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops); + if (err < 0) { + put_device(&dev->dev); return err; } - try_autoload(ops); - unlock_driver(ops); - if (result) *result = dev; @@ -273,82 +264,33 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, EXPORT_SYMBOL(snd_seq_device_new); /* - * free the existing device - */ -static int snd_seq_device_free(struct snd_seq_device *dev) -{ - struct ops_list *ops; - - if (snd_BUG_ON(!dev)) - return -EINVAL; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENXIO; - - /* remove the device from the list */ - mutex_lock(&ops->reg_mutex); - list_del(&dev->list); - ops->num_devices--; - mutex_unlock(&ops->reg_mutex); - - free_device(dev, ops); - if (dev->private_free) - dev->private_free(dev); - kfree(dev); - - unlock_driver(ops); - - return 0; -} - -static int snd_seq_device_dev_free(struct snd_device *device) -{ - struct snd_seq_device *dev = device->device_data; - return snd_seq_device_free(dev); -} - -/* - * register the device + * driver binding - just pass to each driver callback */ -static int snd_seq_device_dev_register(struct snd_device *device) +static int snd_seq_drv_probe(struct device *dev) { - struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - /* initialize this device if the corresponding driver was - * already loaded - */ - if (ops->driver & DRIVER_LOADED) - init_device(dev, ops); + struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); + struct snd_seq_device *sdev = to_seq_dev(dev); + int err; - unlock_driver(ops); + err = sdrv->ops.init_device(sdev); + if (err < 0) + return err; + sdev->status = SNDRV_SEQ_DEVICE_REGISTERED; return 0; } -EXPORT_SYMBOL(snd_seq_device_register_driver); -/* - * disconnect the device - */ -static int snd_seq_device_dev_disconnect(struct snd_device *device) +static int snd_seq_drv_remove(struct device *dev) { - struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - free_device(dev, ops); + struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); + struct snd_seq_device *sdev = to_seq_dev(dev); + int err; - unlock_driver(ops); + err = sdrv->ops.free_device(sdev); + if (err < 0) + return err; + sdev->status = SNDRV_SEQ_DEVICE_FREE; return 0; } -EXPORT_SYMBOL(snd_seq_device_unregister_driver); /* * register device driver @@ -358,226 +300,66 @@ EXPORT_SYMBOL(snd_seq_device_unregister_driver); int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize) { - struct ops_list *ops; - struct snd_seq_device *dev; + struct snd_seq_driver *sdrv; + int err; if (id == NULL || entry == NULL || entry->init_device == NULL || entry->free_device == NULL) return -EINVAL; - ops = find_driver(id, 1); - if (ops == NULL) + sdrv = kzalloc(sizeof(*sdrv), GFP_KERNEL); + if (!sdrv) return -ENOMEM; - if (ops->driver & DRIVER_LOADED) { - pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); - unlock_driver(ops); - return -EBUSY; - } - - mutex_lock(&ops->reg_mutex); - /* copy driver operators */ - ops->ops = *entry; - ops->driver |= DRIVER_LOADED; - ops->argsize = argsize; - /* initialize existing devices if necessary */ - list_for_each_entry(dev, &ops->dev_list, list) { - init_device(dev, ops); - } - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); - - return 0; + sdrv->driver.name = id; + sdrv->driver.bus = &snd_seq_bus_type; + sdrv->driver.probe = snd_seq_drv_probe; + sdrv->driver.remove = snd_seq_drv_remove; + strlcpy(sdrv->id, id, sizeof(sdrv->id)); + sdrv->argsize = argsize; + sdrv->ops = *entry; + + err = driver_register(&sdrv->driver); + if (err < 0) + kfree(sdrv); + return err; } +EXPORT_SYMBOL(snd_seq_device_register_driver); - -/* - * create driver record +/* callback to find a specific driver; data is a pointer to the id string ptr. + * when the id matches, store the driver pointer in return and break the loop. */ -static struct ops_list * create_driver(char *id) +static int find_drv(struct device_driver *drv, void *data) { - struct ops_list *ops; - - ops = kzalloc(sizeof(*ops), GFP_KERNEL); - if (ops == NULL) - return ops; - - /* set up driver entry */ - strlcpy(ops->id, id, sizeof(ops->id)); - mutex_init(&ops->reg_mutex); - /* - * The ->reg_mutex locking rules are per-driver, so we create - * separate per-driver lock classes: - */ - lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id); - - ops->driver = DRIVER_EMPTY; - INIT_LIST_HEAD(&ops->dev_list); - /* lock this instance */ - ops->used = 1; - - /* register driver entry */ - mutex_lock(&ops_mutex); - list_add_tail(&ops->list, &opslist); - num_ops++; - mutex_unlock(&ops_mutex); - - return ops; -} + struct snd_seq_driver *sdrv = to_seq_drv(drv); + void **ptr = (void **)data; + if (strcmp(sdrv->id, *ptr)) + return 0; /* id don't match, continue the loop */ + *ptr = sdrv; + return 1; /* break the loop */ +} /* * unregister the specified driver */ int snd_seq_device_unregister_driver(char *id) { - struct ops_list *ops; - struct snd_seq_device *dev; + struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; - ops = find_driver(id, 0); - if (ops == NULL) + if (!bus_for_each_drv(&snd_seq_bus_type, NULL, &sdrv, find_drv)) return -ENXIO; - if (! (ops->driver & DRIVER_LOADED) || - (ops->driver & DRIVER_LOCKED)) { - pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n", - id, ops->driver); - unlock_driver(ops); - return -EBUSY; - } - - /* close and release all devices associated with this driver */ - mutex_lock(&ops->reg_mutex); - ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ - list_for_each_entry(dev, &ops->dev_list, list) { - free_device(dev, ops); - } - - ops->driver = 0; - if (ops->num_init_devices > 0) - pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n", - ops->num_init_devices); - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); - - /* remove empty driver entries */ - remove_drivers(); - + driver_unregister(&sdrv->driver); + kfree(sdrv); return 0; } - - -/* - * remove empty driver entries - */ -static void remove_drivers(void) -{ - struct list_head *head; - - mutex_lock(&ops_mutex); - head = opslist.next; - while (head != &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); - if (! (ops->driver & DRIVER_LOADED) && - ops->used == 0 && ops->num_devices == 0) { - head = head->next; - list_del(&ops->list); - kfree(ops); - num_ops--; - } else - head = head->next; - } - mutex_unlock(&ops_mutex); -} - -/* - * initialize the device - call init_device operator - */ -static int init_device(struct snd_seq_device *dev, struct ops_list *ops) -{ - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_FREE) - return 0; /* already initialized */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); - return -EINVAL; - } - if (ops->ops.init_device(dev) >= 0) { - dev->status = SNDRV_SEQ_DEVICE_REGISTERED; - ops->num_init_devices++; - } else { - pr_err("ALSA: seq: init_device failed: %s: %s\n", - dev->name, dev->id); - } - - return 0; -} - -/* - * release the device - call free_device operator - */ -static int free_device(struct snd_seq_device *dev, struct ops_list *ops) -{ - int result; - - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) - return 0; /* not registered */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); - return -EINVAL; - } - if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { - dev->status = SNDRV_SEQ_DEVICE_FREE; - dev->driver_data = NULL; - ops->num_init_devices--; - } else { - pr_err("ALSA: seq: free_device failed: %s: %s\n", - dev->name, dev->id); - } - - return 0; -} - -/* - * find the matching driver with given id - */ -static struct ops_list * find_driver(char *id, int create_if_empty) -{ - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if (strcmp(ops->id, id) == 0) { - ops->used++; - mutex_unlock(&ops_mutex); - return ops; - } - } - mutex_unlock(&ops_mutex); - if (create_if_empty) - return create_driver(id); - return NULL; -} - -static void unlock_driver(struct ops_list *ops) -{ - mutex_lock(&ops_mutex); - ops->used--; - mutex_unlock(&ops_mutex); -} - +EXPORT_SYMBOL(snd_seq_device_unregister_driver); /* * module part */ -static int __init alsa_seq_device_init(void) +static int __init seq_dev_proc_init(void) { #ifdef CONFIG_PROC_FS info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", @@ -594,17 +376,28 @@ static int __init alsa_seq_device_init(void) return 0; } +static int __init alsa_seq_device_init(void) +{ + int err; + + err = bus_register(&snd_seq_bus_type); + if (err < 0) + return err; + err = seq_dev_proc_init(); + if (err < 0) + bus_unregister(&snd_seq_bus_type); + return err; +} + static void __exit alsa_seq_device_exit(void) { #ifdef CONFIG_MODULES cancel_work_sync(&autoload_work); #endif - remove_drivers(); #ifdef CONFIG_PROC_FS snd_info_free_entry(info_entry); #endif - if (num_ops) - pr_err("ALSA: seq: drivers not released (%d)\n", num_ops); + bus_unregister(&snd_seq_bus_type); } module_init(alsa_seq_device_init) -- cgit v1.2.3 From af03c243a1f014145dae34368fe975b2f08ed964 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 13:40:50 +0100 Subject: ALSA: seq: Clean up device and driver structs Use const string pointer instead of copying the id string to each object. Also drop the status and list fields of snd_seq_device struct that are no longer used. Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 18 ++++++------------ sound/core/seq/seq_device.c | 31 ++++++++++--------------------- 2 files changed, 16 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index ea256b825146..b13cd2930d32 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -25,24 +25,16 @@ * registered device information */ -#define ID_LEN 32 - -/* status flag */ -#define SNDRV_SEQ_DEVICE_FREE 0 -#define SNDRV_SEQ_DEVICE_REGISTERED 1 - struct snd_seq_device { /* device info */ struct snd_card *card; /* sound card */ int device; /* device number */ - char id[ID_LEN]; /* driver id */ + const char *id; /* driver id */ char name[80]; /* device name */ int argsize; /* size of the argument */ void *driver_data; /* private data for driver */ - int status; /* flag - read only */ void *private_data; /* private data for the caller */ void (*private_free)(struct snd_seq_device *device); - struct list_head list; /* link to next device */ struct device dev; }; @@ -75,9 +67,11 @@ void snd_seq_device_load_drivers(void); #else #define snd_seq_device_load_drivers() #endif -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result); -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize); -int snd_seq_device_unregister_driver(char *id); +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result); +int snd_seq_device_register_driver(const char *id, + struct snd_seq_dev_ops *entry, int argsize); +int snd_seq_device_unregister_driver(const char *id); #define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device)) diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index d3320ffe43c2..49daf6e3a387 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -54,7 +54,7 @@ MODULE_LICENSE("GPL"); struct snd_seq_driver { struct device_driver driver; - char id[ID_LEN]; + const char *id; int argsize; struct snd_seq_dev_ops ops; }; @@ -215,8 +215,8 @@ static void snd_seq_dev_release(struct device *dev) * id = id of driver * result = return pointer (NULL allowed if unnecessary) */ -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, - struct snd_seq_device **result) +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result) { struct snd_seq_device *dev; int err; @@ -239,9 +239,8 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, /* set up device info */ dev->card = card; dev->device = device; - strlcpy(dev->id, id, sizeof(dev->id)); + dev->id = id; dev->argsize = argsize; - dev->status = SNDRV_SEQ_DEVICE_FREE; device_initialize(&dev->dev); dev->dev.parent = &card->card_dev; @@ -270,26 +269,16 @@ static int snd_seq_drv_probe(struct device *dev) { struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); struct snd_seq_device *sdev = to_seq_dev(dev); - int err; - err = sdrv->ops.init_device(sdev); - if (err < 0) - return err; - sdev->status = SNDRV_SEQ_DEVICE_REGISTERED; - return 0; + return sdrv->ops.init_device(sdev); } static int snd_seq_drv_remove(struct device *dev) { struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); struct snd_seq_device *sdev = to_seq_dev(dev); - int err; - err = sdrv->ops.free_device(sdev); - if (err < 0) - return err; - sdev->status = SNDRV_SEQ_DEVICE_FREE; - return 0; + return sdrv->ops.free_device(sdev); } /* @@ -297,8 +286,8 @@ static int snd_seq_drv_remove(struct device *dev) * id = driver id * entry = driver operators - duplicated to each instance */ -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, - int argsize) +int snd_seq_device_register_driver(const char *id, + struct snd_seq_dev_ops *entry, int argsize) { struct snd_seq_driver *sdrv; int err; @@ -315,7 +304,7 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, sdrv->driver.bus = &snd_seq_bus_type; sdrv->driver.probe = snd_seq_drv_probe; sdrv->driver.remove = snd_seq_drv_remove; - strlcpy(sdrv->id, id, sizeof(sdrv->id)); + sdrv->id = id; sdrv->argsize = argsize; sdrv->ops = *entry; @@ -343,7 +332,7 @@ static int find_drv(struct device_driver *drv, void *data) /* * unregister the specified driver */ -int snd_seq_device_unregister_driver(char *id) +int snd_seq_device_unregister_driver(const char *id) { struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; -- cgit v1.2.3 From 056622053b8ae02978678ac1321b5bd956e7c812 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 13:43:22 +0100 Subject: ALSA: seq: Define driver object in each driver This patch moves the driver object initialization and allocation to each driver's module init/exit code like other normal drivers. The snd_seq_driver struct is now published in seq_device.h, and each driver is responsible to define it with proper driver attributes (name, probe and remove) with snd_seq_driver specific attributes as id and argsize fields. The helper functions snd_seq_driver_register(), snd_seq_driver_unregister() and module_snd_seq_driver() are used for simplifying codes. Signed-off-by: Takashi Iwai --- include/sound/seq_device.h | 27 +++++++---- sound/core/seq/oss/seq_oss.c | 20 +++++--- sound/core/seq/oss/seq_oss_synth.c | 6 ++- sound/core/seq/oss/seq_oss_synth.h | 4 +- sound/core/seq/seq_device.c | 93 ++++---------------------------------- sound/core/seq/seq_midi.c | 28 ++++++++---- sound/drivers/opl3/opl3_seq.c | 34 ++++++-------- sound/drivers/opl4/opl4_seq.c | 33 ++++++-------- sound/isa/sb/emu8000_synth.c | 35 ++++++-------- sound/pci/emu10k1/emu10k1_synth.c | 35 ++++++-------- 10 files changed, 124 insertions(+), 191 deletions(-) (limited to 'include') diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index b13cd2930d32..ddc0d504cf39 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -41,8 +41,10 @@ struct snd_seq_device { #define to_seq_dev(_dev) \ container_of(_dev, struct snd_seq_device, dev) +/* sequencer driver */ + /* driver operators - * init_device: + * probe: * Initialize the device with given parameters. * Typically, * 1. call snd_hwdep_new @@ -50,15 +52,19 @@ struct snd_seq_device { * 3. call snd_hwdep_register * 4. store the instance to dev->driver_data pointer. * - * free_device: + * remove: * Release the private data. * Typically, call snd_device_free(dev->card, dev->driver_data) */ -struct snd_seq_dev_ops { - int (*init_device)(struct snd_seq_device *dev); - int (*free_device)(struct snd_seq_device *dev); +struct snd_seq_driver { + struct device_driver driver; + char *id; + int argsize; }; +#define to_seq_drv(_drv) \ + container_of(_drv, struct snd_seq_driver, driver) + /* * prototypes */ @@ -69,12 +75,17 @@ void snd_seq_device_load_drivers(void); #endif int snd_seq_device_new(struct snd_card *card, int device, const char *id, int argsize, struct snd_seq_device **result); -int snd_seq_device_register_driver(const char *id, - struct snd_seq_dev_ops *entry, int argsize); -int snd_seq_device_unregister_driver(const char *id); #define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device)) +int __must_check __snd_seq_driver_register(struct snd_seq_driver *drv, + struct module *mod); +#define snd_seq_driver_register(drv) \ + __snd_seq_driver_register(drv, THIS_MODULE) +void snd_seq_driver_unregister(struct snd_seq_driver *drv); + +#define module_snd_seq_driver(drv) \ + module_driver(drv, snd_seq_driver_register, snd_seq_driver_unregister) /* * id strings for generic devices diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 16d42679e43f..ae1814aa767e 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -65,13 +65,19 @@ static unsigned int odev_poll(struct file *file, poll_table * wait); * module interface */ +static struct snd_seq_driver seq_oss_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_oss_synth_probe, + .remove = snd_seq_oss_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OSS, + .argsize = sizeof(struct snd_seq_oss_reg), +}; + static int __init alsa_seq_oss_init(void) { int rc; - static struct snd_seq_dev_ops ops = { - snd_seq_oss_synth_register, - snd_seq_oss_synth_unregister, - }; snd_seq_autoload_lock(); if ((rc = register_device()) < 0) @@ -86,8 +92,8 @@ static int __init alsa_seq_oss_init(void) goto error; } - if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, - sizeof(struct snd_seq_oss_reg))) < 0) { + rc = snd_seq_driver_register(&seq_oss_synth_driver); + if (rc < 0) { snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); @@ -104,7 +110,7 @@ static int __init alsa_seq_oss_init(void) static void __exit alsa_seq_oss_exit(void) { - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_driver_unregister(&seq_oss_synth_driver); snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 701feb71b700..835edc80f918 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -98,8 +98,9 @@ snd_seq_oss_synth_init(void) * registration of the synth device */ int -snd_seq_oss_synth_register(struct snd_seq_device *dev) +snd_seq_oss_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int i; struct seq_oss_synth *rec; struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -149,8 +150,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) int -snd_seq_oss_synth_unregister(struct snd_seq_device *dev) +snd_seq_oss_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int index; struct seq_oss_synth *rec = dev->driver_data; unsigned long flags; diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h index dbdfcbb80eaa..74ac55f166b6 100644 --- a/sound/core/seq/oss/seq_oss_synth.h +++ b/sound/core/seq/oss/seq_oss_synth.h @@ -28,8 +28,8 @@ #include void snd_seq_oss_synth_init(void); -int snd_seq_oss_synth_register(struct snd_seq_device *dev); -int snd_seq_oss_synth_unregister(struct snd_seq_device *dev); +int snd_seq_oss_synth_probe(struct device *dev); +int snd_seq_oss_synth_remove(struct device *dev); void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp); diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 49daf6e3a387..48b20f009598 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -52,16 +52,6 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -struct snd_seq_driver { - struct device_driver driver; - const char *id; - int argsize; - struct snd_seq_dev_ops ops; -}; - -#define to_seq_drv(_drv) \ - container_of(_drv, struct snd_seq_driver, driver) - /* * bus definition */ @@ -263,86 +253,23 @@ int snd_seq_device_new(struct snd_card *card, int device, const char *id, EXPORT_SYMBOL(snd_seq_device_new); /* - * driver binding - just pass to each driver callback + * driver registration */ -static int snd_seq_drv_probe(struct device *dev) +int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) { - struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); - struct snd_seq_device *sdev = to_seq_dev(dev); - - return sdrv->ops.init_device(sdev); -} - -static int snd_seq_drv_remove(struct device *dev) -{ - struct snd_seq_driver *sdrv = to_seq_drv(dev->driver); - struct snd_seq_device *sdev = to_seq_dev(dev); - - return sdrv->ops.free_device(sdev); -} - -/* - * register device driver - * id = driver id - * entry = driver operators - duplicated to each instance - */ -int snd_seq_device_register_driver(const char *id, - struct snd_seq_dev_ops *entry, int argsize) -{ - struct snd_seq_driver *sdrv; - int err; - - if (id == NULL || entry == NULL || - entry->init_device == NULL || entry->free_device == NULL) + if (WARN_ON(!drv->driver.name || !drv->id)) return -EINVAL; - - sdrv = kzalloc(sizeof(*sdrv), GFP_KERNEL); - if (!sdrv) - return -ENOMEM; - - sdrv->driver.name = id; - sdrv->driver.bus = &snd_seq_bus_type; - sdrv->driver.probe = snd_seq_drv_probe; - sdrv->driver.remove = snd_seq_drv_remove; - sdrv->id = id; - sdrv->argsize = argsize; - sdrv->ops = *entry; - - err = driver_register(&sdrv->driver); - if (err < 0) - kfree(sdrv); - return err; -} -EXPORT_SYMBOL(snd_seq_device_register_driver); - -/* callback to find a specific driver; data is a pointer to the id string ptr. - * when the id matches, store the driver pointer in return and break the loop. - */ -static int find_drv(struct device_driver *drv, void *data) -{ - struct snd_seq_driver *sdrv = to_seq_drv(drv); - void **ptr = (void **)data; - - if (strcmp(sdrv->id, *ptr)) - return 0; /* id don't match, continue the loop */ - *ptr = sdrv; - return 1; /* break the loop */ + drv->driver.bus = &snd_seq_bus_type; + drv->driver.owner = mod; + return driver_register(&drv->driver); } +EXPORT_SYMBOL_GPL(__snd_seq_driver_register); -/* - * unregister the specified driver - */ -int snd_seq_device_unregister_driver(const char *id) +void snd_seq_driver_unregister(struct snd_seq_driver *drv) { - struct snd_seq_driver *sdrv = (struct snd_seq_driver *)id; - - if (!bus_for_each_drv(&snd_seq_bus_type, NULL, &sdrv, find_drv)) - return -ENXIO; - driver_unregister(&sdrv->driver); - kfree(sdrv); - return 0; + driver_unregister(&drv->driver); } -EXPORT_SYMBOL(snd_seq_device_unregister_driver); +EXPORT_SYMBOL_GPL(snd_seq_driver_unregister); /* * module part diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 68fec776da26..79c73119cedc 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) /* register new midi synth port */ static int -snd_seq_midisynth_register_port(struct snd_seq_device *dev) +snd_seq_midisynth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; struct snd_seq_port_info *port; @@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev) /* release midi synth port */ static int -snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) +snd_seq_midisynth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth; struct snd_card *card = dev->card; @@ -457,23 +459,29 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) return 0; } +static struct snd_seq_driver seq_midisynth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_midisynth_probe, + .remove = snd_seq_midisynth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_MIDISYNTH, + .argsize = 0, +}; static int __init alsa_seq_midi_init(void) { - static struct snd_seq_dev_ops ops = { - snd_seq_midisynth_register_port, - snd_seq_midisynth_unregister_port, - }; - memset(&synths, 0, sizeof(synths)); + int err; + snd_seq_autoload_lock(); - snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); + err = snd_seq_driver_register(&seq_midisynth_driver); snd_seq_autoload_unlock(); - return 0; + return err; } static void __exit alsa_seq_midi_exit(void) { - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); + snd_seq_driver_unregister(&seq_midisynth_driver); } module_init(alsa_seq_midi_init) diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index a9f618e06a22..fdae5d7f421f 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3) /* ------------------------------ */ -static int snd_opl3_seq_new_device(struct snd_seq_device *dev) +static int snd_opl3_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; int client, err; char name[32]; @@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl3_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl3_seq_init(void) -{ - static struct snd_seq_dev_ops ops = - { - snd_opl3_seq_new_device, - snd_opl3_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, - sizeof(struct snd_opl3 *)); -} - -static void __exit alsa_opl3_seq_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); -} +static struct snd_seq_driver opl3_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl3_seq_probe, + .remove = snd_opl3_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL3, + .argsize = sizeof(struct snd_opl3 *), +}; -module_init(alsa_opl3_seq_init) -module_exit(alsa_opl3_seq_exit) +module_snd_seq_driver(opl3_seq_driver); diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c index 99197699c55a..03d6202f4829 100644 --- a/sound/drivers/opl4/opl4_seq.c +++ b/sound/drivers/opl4/opl4_seq.c @@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data) snd_midi_channel_free_set(opl4->chset); } -static int snd_opl4_seq_new_device(struct snd_seq_device *dev) +static int snd_opl4_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; int client; struct snd_seq_port_callback pcallbacks; @@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl4_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl4_synth_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_opl4_seq_new_device, - snd_opl4_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops, - sizeof(struct snd_opl4 *)); -} - -static void __exit alsa_opl4_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4); -} +static struct snd_seq_driver opl4_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl4_seq_probe, + .remove = snd_opl4_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL4, + .argsize = sizeof(struct snd_opl4 *), +}; -module_init(alsa_opl4_synth_init) -module_exit(alsa_opl4_synth_exit) +module_snd_seq_driver(opl4_seq_driver); diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 72332dfada9a..4aa719cad331 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -34,8 +34,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu8000 */ -static int snd_emu8000_new_device(struct snd_seq_device *dev) +static int snd_emu8000_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; struct snd_emux *emu; @@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev) /* * free all resources */ -static int snd_emu8000_delete_device(struct snd_seq_device *dev) +static int snd_emu8000_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; if (dev->driver_data == NULL) @@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu8000_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu8000_new_device, - snd_emu8000_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, - sizeof(struct snd_emu8000*)); -} - -static void __exit alsa_emu8000_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); -} - -module_init(alsa_emu8000_init) -module_exit(alsa_emu8000_exit) +static struct snd_seq_driver emu8000_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu8000_probe, + .remove = snd_emu8000_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU8000, + .argsize = sizeof(struct snd_emu8000 *), +}; + +module_snd_seq_driver(emu8000_driver); diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 4c41c903a840..5457d5613f6b 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -29,8 +29,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu10k1 */ -static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; @@ -79,8 +80,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) return 0; } -static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; unsigned long flags; @@ -104,21 +106,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu10k1_synth_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu10k1_synth_new_device, - snd_emu10k1_synth_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, - sizeof(struct snd_emu10k1_synth_arg)); -} - -static void __exit alsa_emu10k1_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); -} - -module_init(alsa_emu10k1_synth_init) -module_exit(alsa_emu10k1_synth_exit) +static struct snd_seq_driver emu10k1_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu10k1_synth_probe, + .remove = snd_emu10k1_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + .argsize = sizeof(struct snd_emu10k1_synth_arg), +}; + +module_snd_seq_driver(emu10k1_synth_driver); -- cgit v1.2.3 From 54a721abd7953a58e5479065c0cfdd8679d785c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Feb 2015 14:20:24 +0100 Subject: ALSA: seq: Drop snd_seq_autoload_lock() and _unlock() The autoload lock became already superfluous due to the recent rework of autoload code. Let's drop them now. This allows us to simplify a few codes nicely. Signed-off-by: Takashi Iwai --- include/sound/seq_kernel.h | 6 +----- sound/core/seq/oss/seq_oss.c | 2 -- sound/core/seq/seq_device.c | 19 +++++++------------ sound/core/seq/seq_dummy.c | 6 +----- sound/core/seq/seq_midi.c | 18 +----------------- 5 files changed, 10 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h index 18a2ac58b88f..feb58d455560 100644 --- a/include/sound/seq_kernel.h +++ b/include/sound/seq_kernel.h @@ -99,13 +99,9 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp, int snd_seq_event_port_detach(int client, int port); #ifdef CONFIG_MODULES -void snd_seq_autoload_lock(void); -void snd_seq_autoload_unlock(void); void snd_seq_autoload_init(void); -#define snd_seq_autoload_exit() snd_seq_autoload_lock() +void snd_seq_autoload_exit(void); #else -#define snd_seq_autoload_lock() -#define snd_seq_autoload_unlock() #define snd_seq_autoload_init() #define snd_seq_autoload_exit() #endif diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index ae1814aa767e..72873a46afeb 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -79,7 +79,6 @@ static int __init alsa_seq_oss_init(void) { int rc; - snd_seq_autoload_lock(); if ((rc = register_device()) < 0) goto error; if ((rc = register_proc()) < 0) { @@ -104,7 +103,6 @@ static int __init alsa_seq_oss_init(void) snd_seq_oss_synth_init(); error: - snd_seq_autoload_unlock(); return rc; } diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 48b20f009598..355b34269bd1 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -98,19 +98,8 @@ static void snd_seq_device_info(struct snd_info_entry *entry, */ #ifdef CONFIG_MODULES -/* avoid auto-loading during module_init() */ +/* flag to block auto-loading */ static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ -void snd_seq_autoload_lock(void) -{ - atomic_inc(&snd_seq_in_init); -} -EXPORT_SYMBOL(snd_seq_autoload_lock); - -void snd_seq_autoload_unlock(void) -{ - atomic_dec(&snd_seq_in_init); -} -EXPORT_SYMBOL(snd_seq_autoload_unlock); static int request_seq_drv(struct device *dev, void *data) { @@ -147,6 +136,12 @@ void snd_seq_autoload_init(void) } EXPORT_SYMBOL(snd_seq_autoload_init); +void snd_seq_autoload_exit(void) +{ + atomic_inc(&snd_seq_in_init); +} +EXPORT_SYMBOL(snd_seq_autoload_exit); + void snd_seq_device_load_drivers(void) { queue_autoload_drivers(); diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 5d905d90d504..d3a2ec4f0561 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -214,11 +214,7 @@ delete_client(void) static int __init alsa_seq_dummy_init(void) { - int err; - snd_seq_autoload_lock(); - err = register_client(); - snd_seq_autoload_unlock(); - return err; + return register_client(); } static void __exit alsa_seq_dummy_exit(void) diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 79c73119cedc..5dd0ee258359 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -469,20 +469,4 @@ static struct snd_seq_driver seq_midisynth_driver = { .argsize = 0, }; -static int __init alsa_seq_midi_init(void) -{ - int err; - - snd_seq_autoload_lock(); - err = snd_seq_driver_register(&seq_midisynth_driver); - snd_seq_autoload_unlock(); - return err; -} - -static void __exit alsa_seq_midi_exit(void) -{ - snd_seq_driver_unregister(&seq_midisynth_driver); -} - -module_init(alsa_seq_midi_init) -module_exit(alsa_seq_midi_exit) +module_snd_seq_driver(seq_midisynth_driver); -- cgit v1.2.3 From 76a3aeac2f6c02ecf065fa9baa279dd54bf2d819 Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:27 +0100 Subject: hdspm.h: include stdint.h in userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes compilation error: sound/hdspm.h:43:2: error: unknown type name ‘uint32_t’ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/hdspm.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index b357f1a5e29c..5737332d38f2 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -20,6 +20,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef __KERNEL__ +#include +#else +#include +#endif + /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ #define HDSPM_MAX_CHANNELS 64 -- cgit v1.2.3 From 4bebf7091aa15ec60edf0dcbc654410a87ca21fe Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:38 +0100 Subject: include/uapi/sound/asound.h: include stdlib.h in userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes compiler errors like: error: field ‘trigger_tstamp’ has incomplete type error: invalid application of ‘sizeof’ to incomplete t ype ‘struct timespec’ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 1f23cd635957..1fe3f4f3d696 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -25,6 +25,9 @@ #include +#ifndef __KERNEL__ +#include +#endif /* * protocol version -- cgit v1.2.3 From bbf91c1c5bfc00c2961f15657359ee7e87de4269 Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:42 +0100 Subject: include/uapi/sound/asequencer.h: include sound/asound.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes userspace compilation error: error: unknown type name ‘snd_seq_client_type_t’ snd_seq_client_type_t type; /* client type */ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/asequencer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index 09c8a00ea503..5a5fa4956ebd 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -22,6 +22,7 @@ #ifndef _UAPI__SOUND_ASEQUENCER_H #define _UAPI__SOUND_ASEQUENCER_H +#include /** version of the sequencer */ #define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1) -- cgit v1.2.3 From b9956409c281931c74ba8d0a2b61a98076a58602 Mon Sep 17 00:00:00 2001 From: Mikko Rapeli Date: Tue, 17 Feb 2015 00:05:43 +0100 Subject: include/uapi/sound/emu10k1.h: include sound/asound.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes userspace compilation errors like: error: field ‘id’ has incomplete type struct snd_ctl_elem_id id; /* full control ID definition */ Signed-off-by: Mikko Rapeli Signed-off-by: Takashi Iwai --- include/uapi/sound/emu10k1.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h index d1bbaf78457a..ec1535bb6aed 100644 --- a/include/uapi/sound/emu10k1.h +++ b/include/uapi/sound/emu10k1.h @@ -23,8 +23,7 @@ #define _UAPI__SOUND_EMU10K1_H #include - - +#include /* * ---- FX8010 ---- -- cgit v1.2.3 From 229d043096ea8e58829d37d35767afeac15997f5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:03 -0600 Subject: ALSA: core: selection of audio_tstamp type and accuracy reports Audio timestamps can be extracted from sample counters, wall clocks, PHC clocks (Ethernet AVB), on-demand synchronized snapshots. This patch provides the ability to report timestamping capabilities, select timestamp types and retrieve timestamp accuracy, if supported. Details can be found in Documentations/sound/alsa/timestamping.txt This functionality is introduced by reclaiming the reserved_aligned field introduced by commit9c7066aef4a5eb8e4063de28f06c508bf6f2963a in snd_pcm_status to provide userspace with selection/query capabilities. Additional driver_tstamp and audio_tstamp_accuracy fields are also added. snd_pcm_mmap_status remains a read-only structure with only the audio timestamp value accessible from user space. The selection of audio timestamp type is done through snd_pcm_status only This commit does not impact ABI and does not impact the default behavior. By default audio timestamp is aligned with hw_pointer and reports the DMA position. Backwards compatibility is handled by using the HDAudio wall clock for playback and the hw_ptr for all other cases. For timestamp selection a new STATUS_EXT ioctl is introduced with read/write parameters. Alsa-lib will be modified to make use of STATUS_EXT. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/timestamping.txt | 200 ++++++++++++++++++++++++++++++ include/sound/pcm.h | 60 +++++++++ include/uapi/sound/asound.h | 34 ++++- 3 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 Documentation/sound/alsa/timestamping.txt (limited to 'include') diff --git a/Documentation/sound/alsa/timestamping.txt b/Documentation/sound/alsa/timestamping.txt new file mode 100644 index 000000000000..0b191a23f534 --- /dev/null +++ b/Documentation/sound/alsa/timestamping.txt @@ -0,0 +1,200 @@ +The ALSA API can provide two different system timestamps: + +- Trigger_tstamp is the system time snapshot taken when the .trigger +callback is invoked. This snapshot is taken by the ALSA core in the +general case, but specific hardware may have synchronization +capabilities or conversely may only be able to provide a correct +estimate with a delay. In the latter two cases, the low-level driver +is responsible for updating the trigger_tstamp at the most appropriate +and precise moment. Applications should not rely solely on the first +trigger_tstamp but update their internal calculations if the driver +provides a refined estimate with a delay. + +- tstamp is the current system timestamp updated during the last +event or application query. +The difference (tstamp - trigger_tstamp) defines the elapsed time. + +The ALSA API provides reports two basic pieces of information, avail +and delay, which combined with the trigger and current system +timestamps allow for applications to keep track of the 'fullness' of +the ring buffer and the amount of queued samples. + +The use of these different pointers and time information depends on +the application needs: + +- 'avail' reports how much can be written in the ring buffer +- 'delay' reports the time it will take to hear a new sample after all +queued samples have been played out. + +When timestamps are enabled, the avail/delay information is reported +along with a snapshot of system time. Applications can select from +CLOCK_REALTIME (NTP corrections including going backwards), +CLOCK_MONOTONIC (NTP corrections but never going backwards), +CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode +dynamically with sw_params + + +The ALSA API also provide an audio_tstamp which reflects the passage +of time as measured by different components of audio hardware. In +ascii-art, this could be represented as follows (for the playback +case): + + +--------------------------------------------------------------> time + ^ ^ ^ ^ ^ + | | | | | + analog link dma app FullBuffer + time time time time time + | | | | | + |< codec delay >|<--hw delay-->||<---avail->| + |<----------------- delay---------------------->| | + |<----ring buffer length---->| + +The analog time is taken at the last stage of the playback, as close +as possible to the actual transducer + +The link time is taken at the output of the SOC/chipset as the samples +are pushed on a link. The link time can be directly measured if +supported in hardware by sample counters or wallclocks (e.g. with +HDAudio 24MHz or PTP clock for networked solutions) or indirectly +estimated (e.g. with the frame counter in USB). + +The DMA time is measured using counters - typically the least reliable +of all measurements due to the bursty natured of DMA transfers. + +The app time corresponds to the time tracked by an application after +writing in the ring buffer. + +The application can query what the hardware supports, define which +audio time it wants reported by selecting the relevant settings in +audio_tstamp_config fields, get an estimate of the timestamp +accuracy. It can also request the delay-to-analog be included in the +measurement. Direct access to the link time is very interesting on +platforms that provide an embedded DSP; measuring directly the link +time with dedicated hardware, possibly synchronized with system time, +removes the need to keep track of internal DSP processing times and +latency. + +In case the application requests an audio tstamp that is not supported +in hardware/low-level driver, the type is overridden as DEFAULT and the +timestamp will report the DMA time based on the hw_pointer value. + +For backwards compatibility with previous implementations that did not +provide timestamp selection, with a zero-valued COMPAT timestamp type +the results will default to the HDAudio wall clock for playback +streams and to the DMA time (hw_ptr) in all other cases. + +The audio timestamp accuracy can be returned to user-space, so that +appropriate decisions are made: + +- for dma time (default), the granularity of the transfers can be + inferred from the steps between updates and in turn provide + information on how much the application pointer can be rewound + safely. + +- the link time can be used to track long-term drifts between audio + and system time using the (tstamp-trigger_tstamp)/audio_tstamp + ratio, the precision helps define how much smoothing/low-pass + filtering is required. The link time can be either reset on startup + or reported as is (the latter being useful to compare progress of + different streams - but may require the wallclock to be always + running and not wrap-around during idle periods). If supported in + hardware, the absolute link time could also be used to define a + precise start time (patches WIP) + +- including the delay in the audio timestamp may + counter-intuitively not increase the precision of timestamps, e.g. if a + codec includes variable-latency DSP processing or a chain of + hardware components the delay is typically not known with precision. + +The accuracy is reported in nanosecond units (using an unsigned 32-bit +word), which gives a max precision of 4.29s, more than enough for +audio applications... + +Due to the varied nature of timestamping needs, even for a single +application, the audio_tstamp_config can be changed dynamically. In +the STATUS ioctl, the parameters are read-only and do not allow for +any application selection. To work around this limitation without +impacting legacy applications, a new STATUS_EXT ioctl is introduced +with read/write parameters. ALSA-lib will be modified to make use of +STATUS_EXT and effectively deprecate STATUS. + +The ALSA API only allows for a single audio timestamp to be reported +at a time. This is a conscious design decision, reading the audio +timestamps from hardware registers or from IPC takes time, the more +timestamps are read the more imprecise the combined measurements +are. To avoid any interpretation issues, a single (system, audio) +timestamp is reported. Applications that need different timestamps +will be required to issue multiple queries and perform an +interpolation of the results + +In some hardware-specific configuration, the system timestamp is +latched by a low-level audio subsytem, and the information provided +back to the driver. Due to potential delays in the communication with +the hardware, there is a risk of misalignment with the avail and delay +information. To make sure applications are not confused, a +driver_timestamp field is added in the snd_pcm_status structure; this +timestamp shows when the information is put together by the driver +before returning from the STATUS and STATUS_EXT ioctl. in most cases +this driver_timestamp will be identical to the regular system tstamp. + +Examples of typestamping with HDaudio: + +1. DMA timestamp, no compensation for DMA+analog delay +$ ./audio_time -p --ts_type=1 +playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662 +playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837 +playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420 +playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051 +playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751 +playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822 + +2. DMA timestamp, compensation for DMA+analog delay +$ ./audio_time -p --ts_type=1 -d +playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153 +playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947 +playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685 +playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349 +playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694 + +3. link timestamp, compensation for DMA+analog delay +$ ./audio_time -p --ts_type=2 -d +playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787 +playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801 +playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591 +playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779 +playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687 +playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146 + +Example 1 shows that the timestamp at the DMA level is close to 1ms +ahead of the actual playback time (as a side time this sort of +measurement can help define rewind safeguards). Compensating for the +DMA-link delay in example 2 helps remove the hardware buffering abut +the information is still very jittery, with up to one sample of +error. In example 3 where the timestamps are measured with the link +wallclock, the timestamps show a monotonic behavior and a lower +dispersion. + +Example 3 and 4 are with USB audio class. Example 3 shows a high +offset between audio time and system time due to buffering. Example 4 +shows how compensating for the delay exposes a 1ms accuracy (due to +the use of the frame counter by the driver) + +Example 3: DMA timestamp, no compensation for delay, delta of ~5ms +$ ./audio_time -p -Dhw:1 -t1 +playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981 +playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864 +playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912 +playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935 +playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821 +playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259 +playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664 + +Example 4: DMA timestamp, compensation for delay, delay of ~1ms +$ ./audio_time -p -Dhw:1 -t1 -d +playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520 +playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740 +playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081 +playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907 +playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824 +playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847 diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c0ddb7e69c28..60f0e48f7905 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -60,6 +60,9 @@ struct snd_pcm_hardware { struct snd_pcm_substream; +struct snd_pcm_audio_tstamp_config; /* definitions further down */ +struct snd_pcm_audio_tstamp_report; + struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); @@ -281,6 +284,58 @@ struct snd_pcm_hw_constraint_ranges { struct snd_pcm_hwptr_log; +/* + * userspace-provided audio timestamp config to kernel, + * structure is for internal use only and filled with dedicated unpack routine + */ +struct snd_pcm_audio_tstamp_config { + /* 5 of max 16 bits used */ + u32 type_requested:4; + u32 report_delay:1; /* add total delay to A/D or D/A */ +}; + +static inline void snd_pcm_unpack_audio_tstamp_config(__u32 data, + struct snd_pcm_audio_tstamp_config *config) +{ + config->type_requested = data & 0xF; + config->report_delay = (data >> 4) & 1; +} + +/* + * kernel-provided audio timestamp report to user-space + * structure is for internal use only and read by dedicated pack routine + */ +struct snd_pcm_audio_tstamp_report { + /* 6 of max 16 bits used for bit-fields */ + + /* for backwards compatibility */ + u32 valid:1; + + /* actual type if hardware could not support requested timestamp */ + u32 actual_type:4; + + /* accuracy represented in ns units */ + u32 accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */ + u32 accuracy; /* up to 4.29s, will be packed in separate field */ +}; + +static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy, + const struct snd_pcm_audio_tstamp_report *report) +{ + u32 tmp; + + tmp = report->accuracy_report; + tmp <<= 4; + tmp |= report->actual_type; + tmp <<= 1; + tmp |= report->valid; + + *data &= 0xffff; /* zero-clear MSBs */ + *data |= (tmp << 16); + *accuracy = report->accuracy; +} + + struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; @@ -361,6 +416,11 @@ struct snd_pcm_runtime { struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ + /* -- audio timestamp config -- */ + struct snd_pcm_audio_tstamp_config audio_tstamp_config; + struct snd_pcm_audio_tstamp_report audio_tstamp_report; + struct timespec driver_tstamp; + #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 0e88e7a0f0eb..acef4e4d2735 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -267,10 +267,17 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ -#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* (Deprecated)has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000 /* report hardware link audio time, reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ +#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ + #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ + + typedef int __bitwise snd_pcm_state_t; #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ #define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */ @@ -408,6 +415,22 @@ struct snd_pcm_channel_info { unsigned int step; /* samples distance in bits */ }; +enum { + /* + * first definition for backwards compatibility only, + * maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else + */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0, + + /* timestamp definitions */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /* DMA time, reported as per hw_ptr */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /* link time reported by sample or wallclock counter, reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /* link time reported by sample or wallclock counter, not reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /* link time estimated indirectly */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED +}; + struct snd_pcm_status { snd_pcm_state_t state; /* stream state */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ @@ -419,9 +442,11 @@ struct snd_pcm_status { snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ snd_pcm_state_t suspended_state; /* suspended stream state */ - __u32 reserved_alignment; /* must be filled with zero */ - struct timespec audio_tstamp; /* from sample counter or wall clock */ - unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */ + __u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */ + struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */ + struct timespec driver_tstamp; /* useful in case reference system tstamp is reported with delay */ + __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ + unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */ }; struct snd_pcm_mmap_status { @@ -534,6 +559,7 @@ enum { #define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) #define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) #define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr) +#define SNDRV_PCM_IOCTL_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status) #define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info) #define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40) #define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41) -- cgit v1.2.3 From 3179f62001880e588e229db3006a59ad87b7792a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:06 -0600 Subject: ALSA: core: add .get_time_info Introduce more generic .get_time_info to retrieve system timestamp and audio timestamp in single routine. Backwards compatibility is preserved with same functionality as with .wall_clock method (to be removed in following commits to avoid breaking git bisect) Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 4 +++ sound/core/pcm_lib.c | 88 +++++++++++++++++++++++++++++++++---------------- sound/core/pcm_native.c | 24 ++++++++++++++ 3 files changed, 87 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 60f0e48f7905..04f2d492ae57 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -76,6 +76,10 @@ struct snd_pcm_ops { snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); int (*wall_clock)(struct snd_pcm_substream *substream, struct timespec *audio_ts); + int (*get_time_info)(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report); int (*copy)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ffd656012ab8..ac6b33f3779c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, return 0; } +static void update_audio_tstamp(struct snd_pcm_substream *substream, + struct timespec *curr_tstamp, + struct timespec *audio_tstamp) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u64 audio_frames, audio_nsecs; + struct timespec driver_tstamp; + + if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) + return; + + if (!(substream->ops->get_time_info) || + (runtime->audio_tstamp_report.actual_type == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + + /* + * provide audio timestamp derived from pointer position + * add delay only if requested + */ + + audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; + + if (runtime->audio_tstamp_config.report_delay) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + audio_frames -= runtime->delay; + else + audio_frames += runtime->delay; + } + audio_nsecs = div_u64(audio_frames * 1000000000LL, + runtime->rate); + *audio_tstamp = ns_to_timespec(audio_nsecs); + } + runtime->status->audio_tstamp = *audio_tstamp; + runtime->status->tstamp = *curr_tstamp; + + /* + * re-take a driver timestamp to let apps detect if the reference tstamp + * read by low-level hardware was provided with a delay + */ + snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); + runtime->driver_tstamp = driver_tstamp; +} + static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, unsigned int in_interrupt) { @@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, pos = substream->ops->pointer(substream); curr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); - - if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && - (substream->ops->wall_clock)) - substream->ops->wall_clock(substream, &audio_tstamp); + if ((substream->ops->get_time_info) && + (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + substream->ops->get_time_info(substream, &curr_tstamp, + &audio_tstamp, + &runtime->audio_tstamp_config, + &runtime->audio_tstamp_report); + + /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ + if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + } else + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); } if (pos == SNDRV_PCM_POS_XRUN) { @@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } no_delta_check: - if (runtime->status->hw_ptr == new_hw_ptr) + if (runtime->status->hw_ptr == new_hw_ptr) { + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return 0; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_BUG_ON(crossed_boundary != 1); runtime->hw_ptr_wrap += runtime->boundary; } - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - runtime->status->tstamp = curr_tstamp; - if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { - /* - * no wall clock available, provide audio timestamp - * derived from pointer position+delay - */ - u64 audio_frames, audio_nsecs; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - - runtime->delay; - else - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - + runtime->delay; - audio_nsecs = div_u64(audio_frames * 1000000000LL, - runtime->rate); - audio_tstamp = ns_to_timespec(audio_nsecs); - } - runtime->status->audio_tstamp = audio_tstamp; - } + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return snd_pcm_update_state(substream, runtime); } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 72323a8c35c8..9c2c6f801fed 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); + + snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data, + &runtime->audio_tstamp_config); + + /* backwards compatible behavior */ + if (runtime->audio_tstamp_config.type_requested == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) { + if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + else + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + runtime->audio_tstamp_report.valid = 0; + } else + runtime->audio_tstamp_report.valid = 1; + status->state = runtime->status->state; status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) @@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->driver_tstamp = runtime->driver_tstamp; status->audio_tstamp = runtime->status->audio_tstamp; + if (runtime->audio_tstamp_report.valid == 1) + /* backwards compatibility, no report provided in COMPAT mode */ + snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, + &status->audio_tstamp_accuracy, + &runtime->audio_tstamp_report); + goto _tstamp_end; } } else { -- cgit v1.2.3 From 2d52a5abd5be35340296a251092c8a594679df54 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:08 -0600 Subject: ALSA: core: remove .wall_clock can be removed without breaking git-bisect now Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 04f2d492ae57..0cb7f3f5df7b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -74,8 +74,6 @@ struct snd_pcm_ops { int (*prepare)(struct snd_pcm_substream *substream); int (*trigger)(struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); - int (*wall_clock)(struct snd_pcm_substream *substream, - struct timespec *audio_ts); int (*get_time_info)(struct snd_pcm_substream *substream, struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, -- cgit v1.2.3 From c72638bdaabe9ea4b09003b9db7e1754f472fbed Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 13 Feb 2015 15:14:09 -0600 Subject: ALSA: bump PCM protocol to 2.0.13 Bump PCM protocol to enable use of STATUS_EXT ioctls, older apps will still use STATUS and audio timestamp configuration is not supported (backwards compatible behavior). Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index acef4e4d2735..3d46e9a0cd2e 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -140,7 +140,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; -- cgit v1.2.3 From 052a9f698268e606ca01eb1ce2a672e548f2ce11 Mon Sep 17 00:00:00 2001 From: "Fang, Yang A" Date: Mon, 9 Feb 2015 00:18:11 -0800 Subject: ALSA: Add params_set_format helper Add a helper to set pcm format directly from params Signed-off-by: Fang, Yang A Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/pcm_params.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 3c45f3924ba7..c704357775fc 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -366,4 +366,11 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p) return snd_pcm_format_physical_width(params_format(p)); } +static inline void +params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt) +{ + snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT), + (__force int)fmt); +} + #endif /* __SOUND_PCM_PARAMS_H */ -- cgit v1.2.3 From 48c7699fb2c799d084ce490bceea14fe04ad12a1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 Feb 2015 09:59:53 +0530 Subject: ASoC: core: allow pcms to be registered as nonatomic ALSA core with commit 257f8cce5d40 - "ALSA: pcm: Allow nonatomic trigger operations" allows trigger ops to implemented as nonatomic. For ASoC, we can specify this in dailinks and is updated while snd_pcm is created Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Cc: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ sound/soc/soc-pcm.c | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..76bc944dcb5c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -954,6 +954,9 @@ struct snd_soc_dai_link { unsigned int symmetric_channels:1; unsigned int symmetric_samplebits:1; + /* Mark this pcm with non atomic ops */ + bool nonatomic; + /* Do not create a PCM for this DAI link (Backend link) */ unsigned int no_pcm:1; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6b0136e7cb88..6e3781e88f9a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2511,6 +2511,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; pcm->private_data = rtd; -- cgit v1.2.3 From f23e860edbb3f2208c0ab3448e756689bb4a3760 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 14 Feb 2015 17:22:49 -0800 Subject: ASoC: core: Add extra dapm properties for Device Tree The current helper functions, snd_soc_of_parse_audio_simple_widgets() and snd_soc_of_parse_audio_routing(), set dapm_widgets and dapm_routes without caring if they are already set by using build-in widgets and routes in the card driver. So there could be one of them, build-in one or Device Tree one, overrided by the other depending on which one was assigned later. This patch adds an extra pair of dapm_widgets and dapm_routes for DT use only so as to prevent unexpected overriding. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- include/sound/soc.h | 5 +++++ sound/soc/soc-core.c | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..f66a1ef98a40 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1071,11 +1071,16 @@ struct snd_soc_card { /* * Card-specific routes and widgets. + * Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in. */ const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; + const struct snd_soc_dapm_widget *of_dapm_widgets; + int num_of_dapm_widgets; + const struct snd_soc_dapm_route *of_dapm_routes; + int num_of_dapm_routes; bool fully_routed; struct work_struct deferred_resume_work; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 30579ca5bacb..5c0658d49609 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1561,6 +1561,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); + if (card->of_dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, + card->num_of_dapm_widgets); + /* initialise the sound card only once */ if (card->probe) { ret = card->probe(card); @@ -1616,6 +1620,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); + if (card->of_dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, + card->num_of_dapm_routes); + for (i = 0; i < card->num_links; i++) { if (card->dai_link[i].dai_fmt) snd_soc_runtime_set_dai_fmt(&card->rtd[i], @@ -3223,8 +3231,8 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, widgets[i].name = wname; } - card->dapm_widgets = widgets; - card->num_dapm_widgets = num_widgets; + card->of_dapm_widgets = widgets; + card->num_of_dapm_widgets = num_widgets; return 0; } @@ -3308,8 +3316,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes = num_routes; - card->dapm_routes = routes; + card->num_of_dapm_routes = num_routes; + card->of_dapm_routes = routes; return 0; } -- cgit v1.2.3 From e086e3035e0691b362755d1b5e24df631eee335a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Feb 2015 18:01:22 +0100 Subject: ALSA: core: Re-add snd_device_disconnect() Revive snd_device_disconnect() again so that it can be called from the individual driver. This time, HD-audio will need it. Signed-off-by: Takashi Iwai --- include/sound/core.h | 3 ++- sound/core/device.c | 43 +++++++++++++++++++++++++++++++++---------- sound/core/init.c | 5 +---- 3 files changed, 36 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index da5748289968..b12931f513f4 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -278,7 +278,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, void *device_data, struct snd_device_ops *ops); int snd_device_register(struct snd_card *card, void *device_data); int snd_device_register_all(struct snd_card *card); -int snd_device_disconnect_all(struct snd_card *card); +void snd_device_disconnect(struct snd_card *card, void *device_data); +void snd_device_disconnect_all(struct snd_card *card); void snd_device_free(struct snd_card *card, void *device_data); void snd_device_free_all(struct snd_card *card); diff --git a/sound/core/device.c b/sound/core/device.c index 41bec3075ae5..446dc452654e 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -73,7 +73,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, } EXPORT_SYMBOL(snd_device_new); -static int __snd_device_disconnect(struct snd_device *dev) +static void __snd_device_disconnect(struct snd_device *dev) { if (dev->state == SNDRV_DEV_REGISTERED) { if (dev->ops->dev_disconnect && @@ -81,7 +81,6 @@ static int __snd_device_disconnect(struct snd_device *dev) dev_err(dev->card->dev, "device disconnect failure\n"); dev->state = SNDRV_DEV_DISCONNECTED; } - return 0; } static void __snd_device_free(struct snd_device *dev) @@ -108,6 +107,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) return NULL; } +/** + * snd_device_disconnect - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Return: Zero if successful, or a negative error code on failure or if the + * device not found. + */ +void snd_device_disconnect(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + if (snd_BUG_ON(!card || !device_data)) + return; + dev = look_for_dev(card, device_data); + if (dev) + __snd_device_disconnect(dev); + else + dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n", + device_data, __builtin_return_address(0)); +} +EXPORT_SYMBOL_GPL(snd_device_disconnect); + /** * snd_device_free - release the device from the card * @card: the card instance @@ -195,18 +222,14 @@ int snd_device_register_all(struct snd_card *card) * disconnect all the devices on the card. * called from init.c */ -int snd_device_disconnect_all(struct snd_card *card) +void snd_device_disconnect_all(struct snd_card *card) { struct snd_device *dev; - int err = 0; if (snd_BUG_ON(!card)) - return -ENXIO; - list_for_each_entry_reverse(dev, &card->devices, list) { - if (__snd_device_disconnect(dev) < 0) - err = -ENXIO; - } - return err; + return; + list_for_each_entry_reverse(dev, &card->devices, list) + __snd_device_disconnect(dev); } /* diff --git a/sound/core/init.c b/sound/core/init.c index 35419054821c..04734e047bfe 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops = int snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; - int err; if (!card) return -EINVAL; @@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card) #endif /* notify all devices that we are disconnected */ - err = snd_device_disconnect_all(card); - if (err < 0) - dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); + snd_device_disconnect_all(card); snd_info_card_disconnect(card); if (card->registered) { -- cgit v1.2.3 From 1a6ab46fa9c2bc9399694b4856ab7ea19c036485 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 4 Mar 2015 10:56:13 +0900 Subject: ALSA: Fix spelling typo in Documentation/DocBook/alsa-driver-api.xml This patch fix spelling typo found in alsa-driver-api.xml. It is because this file is generated from comments in source files, I have to fix source files. Signed-off-by: Masanari Iida Signed-off-by: Takashi Iwai --- include/sound/compress_driver.h | 4 ++-- include/sound/control.h | 2 +- include/sound/soc.h | 2 +- include/uapi/sound/compress_offload.h | 2 +- sound/core/pcm_dmaengine.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index f48089d364c5..fa1d05512c09 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -70,7 +70,7 @@ struct snd_compr_runtime { * @device: device pointer * @direction: stream direction, playback/recording * @metadata_set: metadata set flag, true when set - * @next_track: has userspace signall next track transistion, true when set + * @next_track: has userspace signal next track transition, true when set * @private_data: pointer to DSP private data */ struct snd_compr_stream { @@ -95,7 +95,7 @@ struct snd_compr_stream { * and the stream properties * @get_params: retrieve the codec parameters, mandatory * @set_metadata: Set the metadata values for a stream - * @get_metadata: retreives the requested metadata values from stream + * @get_metadata: retrieves the requested metadata values from stream * @trigger: Trigger operations like start, pause, resume, drain, stop. * This callback is mandatory * @pointer: Retrieve current h/w pointer information. Mandatory diff --git a/include/sound/control.h b/include/sound/control.h index 75f3054023f7..95aad6d3fd1a 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -227,7 +227,7 @@ snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) * Add a virtual slave control to the given master. * Unlike snd_ctl_add_slave(), the element added via this function * is supposed to have volatile values, and get callback is called - * at each time quried from the master. + * at each time queried from the master. * * When the control peeks the hardware values directly and the value * can be changed by other means than the put callback of the element, diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..cf0bb156d6da 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1469,7 +1469,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( } /** - * snd_soc_kcontrol_platform() - Returns the platform that registerd the control + * snd_soc_kcontrol_platform() - Returns the platform that registered the control * @kcontrol: The control for which to get the platform * * Note: This function will only work correctly if the control has been diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 22ed8cb7800b..e00d8cbfc628 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -75,7 +75,7 @@ struct snd_compr_tstamp { /** * struct snd_compr_avail - avail descriptor * @avail: Number of bytes available in ring buffer for writing/reading - * @tstamp: timestamp infomation + * @tstamp: timestamp information */ struct snd_compr_avail { __u64 avail; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 6542c4083594..fba365a78390 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); * * The function should usually be called from the pcm open callback. Note that * this function will use private_data field of the substream's runtime. So it - * is not availabe to your pcm driver implementation. + * is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan) @@ -328,7 +328,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); * This function will request a DMA channel using the passed filter function and * data. The function should usually be called from the pcm open callback. Note * that this function will use private_data field of the substream's runtime. So - * it is not availabe to your pcm driver implementation. + * it is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data) -- cgit v1.2.3 From 970939964c26db4643f58d4e84487821962e0b65 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 4 Mar 2015 10:33:17 +0100 Subject: ASoC: Allow to register jacks at the card level Jacks are typically card level elements, but are currently registered with a CODEC. When it was originally introduced snd_soc_jack_new() took a snd_soc_card as its parameter, but at that time DAPM was only implemented at the CODEC level and there was only one CODEC per card. This made it clear which CODEC to use for the jack DAPM operations. But the multi-component patchset added support for having multiple CODECs per card and with it the API was updated to register jacks with a specific CODEC instance instead. Subsequently DAPM support at the card level has been introduced, but the snd_soc_jack_new() API has so remained unchanged. This leaves us with the issue that the DAPM pins that are managed by the jack detection logic usually are part of the card DAPM context but are accessed through a CODEC DAPM context. Currently this works fine, but might break in the future if we take a more hierarchical approach to DAPM contexts. Furthermore with componentization progressing systems that do not register a snd_soc_codec might appear, while these system may still want to able to register a jack. This patch addresses these issues by adding a new function called snd_soc_card_jack_new() that can be used to register jacks with the card rather than a CODEC. This new function is mostly identical to snd_soc_jack_new() except that it additionally allows to directly specify the DAPM pins associated with the jack. This was done since most users of snd_soc_jack_new() typically call snd_soc_jack_add_pins() right after it, which is not necessary with the new API and allows to reduce the amount of boiler plate code. The old snd_soc_jack_new() is re-implemented as a wrapper around snd_soc_card_jack_new(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 28 +++++++++++++++++++++++++--- sound/soc/soc-jack.c | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..99d9ce272209 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -450,8 +450,10 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); /* Jack reporting */ -int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, - struct snd_soc_jack *jack); +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins); + void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask); int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, struct snd_soc_jack_pin *pins); @@ -659,7 +661,7 @@ struct snd_soc_jack_gpio { struct snd_soc_jack { struct mutex mutex; struct snd_jack *jack; - struct snd_soc_codec *codec; + struct snd_soc_card *card; struct list_head pins; int status; struct blocking_notifier_head notifier; @@ -1482,6 +1484,26 @@ static inline struct snd_soc_platform *snd_soc_kcontrol_platform( return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol)); } +/** + * snd_soc_jack_new - Create a new jack + * @codec: ASoC CODEC + * @id: an identifying string for this jack + * @type: a bitmask of enum snd_jack_type values that can be detected by + * this jack + * @jack: structure to use for the jack + * + * Creates a new jack object. + * + * Returns zero if successful, or a negative error code on failure. + * On success jack will be initialised. + */ +static inline int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, + int type, struct snd_soc_jack *jack) +{ + return snd_soc_card_jack_new(codec->component.card, id, type, jack, + NULL, 0); +} + int snd_soc_util_init(void); void snd_soc_util_exit(void); diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 4380dcc064a5..9f60c25c4568 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -22,30 +22,42 @@ #include /** - * snd_soc_jack_new - Create a new jack - * @codec: ASoC codec + * snd_soc_card_jack_new - Create a new jack + * @card: ASoC card * @id: an identifying string for this jack * @type: a bitmask of enum snd_jack_type values that can be detected by * this jack * @jack: structure to use for the jack + * @pins: Array of jack pins to be added to the jack or NULL + * @num_pins: Number of elements in the @pins array * * Creates a new jack object. * * Returns zero if successful, or a negative error code on failure. * On success jack will be initialised. */ -int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, - struct snd_soc_jack *jack) +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins) { + int ret; + mutex_init(&jack->mutex); - jack->codec = codec; + jack->card = card; INIT_LIST_HEAD(&jack->pins); INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack); + ret = snd_jack_new(card->snd_card, id, type, &jack->jack); + if (ret) + return ret; + + if (num_pins) + return snd_soc_jack_add_pins(jack, num_pins, pins); + + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_jack_new); +EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); /** * snd_soc_jack_report - Report the current status for a jack @@ -63,7 +75,6 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new); */ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) { - struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; unsigned int sync = 0; @@ -74,8 +85,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) if (!jack) return; - codec = jack->codec; - dapm = &codec->dapm; + dapm = &jack->card->dapm; mutex_lock(&jack->mutex); @@ -175,12 +185,12 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!pins[i].pin) { - dev_err(jack->codec->dev, "ASoC: No name for pin %d\n", + dev_err(jack->card->dev, "ASoC: No name for pin %d\n", i); return -EINVAL; } if (!pins[i].mask) { - dev_err(jack->codec->dev, "ASoC: No mask for pin %d" + dev_err(jack->card->dev, "ASoC: No mask for pin %d" " (%s)\n", i, pins[i].pin); return -EINVAL; } @@ -260,7 +270,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) static irqreturn_t gpio_handler(int irq, void *data) { struct snd_soc_jack_gpio *gpio = data; - struct device *dev = gpio->jack->codec->component.card->dev; + struct device *dev = gpio->jack->card->dev; trace_snd_soc_jack_irq(gpio->name); @@ -299,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!gpios[i].name) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: No name for gpio at index %d\n", i); ret = -EINVAL; goto undo; @@ -320,7 +330,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, } else { /* legacy GPIO number */ if (!gpio_is_valid(gpios[i].gpio)) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Invalid gpio %d\n", gpios[i].gpio); ret = -EINVAL; @@ -350,7 +360,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (gpios[i].wake) { ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", i, ret); } -- cgit v1.2.3 From 77c71765ef78f87dd901fcb4c751908e835a3b2e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 4 Mar 2015 10:33:45 +0100 Subject: ASoC: Remove snd_soc_jack_new() There are no users of snd_soc_jack_new() left and new users should use snd_soc_card_jack_new() instead. So remove the function. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 99d9ce272209..40b3ee96f317 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1484,26 +1484,6 @@ static inline struct snd_soc_platform *snd_soc_kcontrol_platform( return snd_soc_component_to_platform(snd_soc_kcontrol_component(kcontrol)); } -/** - * snd_soc_jack_new - Create a new jack - * @codec: ASoC CODEC - * @id: an identifying string for this jack - * @type: a bitmask of enum snd_jack_type values that can be detected by - * this jack - * @jack: structure to use for the jack - * - * Creates a new jack object. - * - * Returns zero if successful, or a negative error code on failure. - * On success jack will be initialised. - */ -static inline int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, - int type, struct snd_soc_jack *jack) -{ - return snd_soc_card_jack_new(codec->component.card, id, type, jack, - NULL, 0); -} - int snd_soc_util_init(void); void snd_soc_util_exit(void); -- cgit v1.2.3